BenDefinitionRegistryPostProcessorとダイナミックエージェントの配合使用例

28402 ワード

このような機能を実現します.注釈をカスタマイズしました.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyReference{
}
注释されたフィールドは私たちが代理したいクラスです.Spring起動時にプロキシクラスをこの注釈で識別されたフィールドに注入したいです.
どのように実現しますか
Spring起動後に@MyReferenceに表示されるクラスの交替はどうやって実現しますか?
プロキシ類のbeanDefinationを容器に登録し、委託類の容器にあるnameと関連させて容器に注入するのがプロキシ類です.BeanDefinitionRegistryPostProcessorを用いて実現される.
ここでは、私たちのエージェントの機能は同じです.委託クラスの情報を取得して、要求を遠端に送信して結果を得るので、FactoryBeanInitializingBeanを利用して、ProxyFactoryBenというクラスを作成して、それらを実現します.内部は委託クラスのタイプを識別するためにメンバー変数を宣言します.この方法はbeanを初期化するときに呼び出される.各表示類には、ProxyFactoryBeanタイプのbeanDefinationが作成されます.
このように、私達のプロジェクトのすべての表示されているクラスは、Spring容器の中のBeanDefinationは実質的にFactoryBeanであり、SpringはBenのインスタンスを取得する時にFactoryBenであると発見されたら、そのgetObjectを呼び出します.私達はこの方法の中でタイプによってこの表示類の代理種類のオブジェクトを作成して、getObjectが返したのもそれです.具体的なコードは下記の通り実現されます.
2,私たちはSpringBootを使っていますが、上記の機能は独立したモジュールとして、プロジェクトに依存しています.容器起動時にトリガする必要があります.どうやってトリガしますか?Spring BootのSpring Factores機構を利用して実現します.
Spring Factoresについて、二つの文章を紹介します.Spring Bootの拡張メカニズムのSpring Factores Everable AutoConfigrationの注釈の仕事原理
機能モジュールのresourceディレクトリの下でMETA-INF/spring.factores
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.miao.client.MyAutoConfiguration
MyAutoConfigration
@Configuration
@ConditionalOnMissingBean(ProxyFactoryBeanRegistry.class)
@EnableConfigurationProperties(ClientProperties.class)
public class MyAutoConfiguration{

    @Autowired
    private ClientProperties properties;
    
    //     static?      proxyFactoryBeanRegistry      client
    //        static ,   @Configuration   , @Bean      
    //      BeanDefinitionRegistryPostProcessor,       static 
    // https://github.com/ulisesbocchio/jasypt-spring-boot/issues/45
    //https://stackoverflow.com/questions/41939494/springboot-cannot-enhance-configuration-bean-definition-beannameplaceholderreg
    private static Client client = new Client();

    @Bean
    public Client client() {
        log.info("   Client  discovery");
        ServiceDiscovery discovery = ......
        client.setDiscovery(discovery);
        client.init();
        return client;
    }
    
    /**
     * Cannot enhance @Configuration bean definition 'com.miao.rpc.client.RpcClientAutoConfiguration'
     * since its singleton instance has been created too early.
     * The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type:
     * Consider declaring such methods as 'static'.
     *
     *  @Configuration   , @Bean           BeanDefinitionRegistryPostProcessor,       static 
     *      : 
     * Having a BeanFactoryPostProcessor in a @Configuration class breaks the default post-processing 
     * of that @Configuration class.
     */
    @Bean
    public static ProxyFactoryBeanRegistry proxyFactoryBeanRegistry(){
    	//        property     ,              
        String basePackage = PropertityUtil.getProperty("rpc.serverBasePackage");
        return new ProxyFactoryBeanRegistry(basePackage, client);
    }
次にBenDefinitionRegistryPostProcessorを利用して、bean初期化前に委託類のbeanDefinitionを代理類のbeanDefinitionに変えます.委託類は@MyReferenceに表示され、それを私達に向けたproxyエージェント類を実現します.それらの呼び出しは実際に私達の代理類に呼び出されます.
/**
 *        BeanDefinitionRegistryPostProcessor,     BeanFactoryPostProcessor
 * BeanFactoryPostProcessor        bean    bean   (     ),      。
 * BeanDefinitionRegistryPostProcessor       bean    
 *
 *               beanDefinition,               ,
 *   @Autowired              
 */
@Slf4j
public class ProxyFactoryBeanRegistry implements BeanDefinitionRegistryPostProcessor {
    private String basePackage;
    private Client client;

    public ProxyFactoryBeanRegistry(String basePackage, Client client) {
        this.basePackage = basePackage;
        this.client = client;
    }

    /**
     *            ,   class  ,  class      @RpcReference     ,
     *       RpcProxyFactoryBean   beanDefinition,      beanName  ,
     * RpcProxyFactoryBean  FactoryBean,          getObject      bean,
     *     RpcProxyFactoryBean     InitializingBean,          afterPropertiesSet  ,
     *                 ,         @Autowired         @RpcReference     ,
     *              ,          invoke  ,         ,   ,   ,  
     *          
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        log.info("          FactoryBean");
        //      
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true); //       ,      
        Set<BeanDefinition> beanDefinitionSet = scanner.findCandidateComponents(basePackage); //          
        for (BeanDefinition beanDefinition : beanDefinitionSet) {
            log.info("        {}", beanDefinition.getBeanClassName());
            String beanClassName = beanDefinition.getBeanClassName(); //   class name
            Class<?> beanClass = null;
            try {
                beanClass = Class.forName(beanClassName); //   Class  
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            Field[] fields = beanClass.getDeclaredFields(); //    Class   field
            for (Field field : fields) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                // @MyReference    
                MyReference reference = field.getAnnotation(MyReference.class);
                Class<?> fieldClass = field.getType(); //            ,      proxy
                if (reference != null) {
                    log.info("  " + fieldClass.getName() + "     ");
                    BeanDefinitionHolder holder = createBeanDefinition(fieldClass);
                    log.info("    ");
                    //      beanDefination      
                    BeanDefinitionReaderUtils.registerBeanDefinition(holder, beanDefinitionRegistry);
                }
            }

        }
    }

    /**
     *   fieldClass   BeanDefinition
     * @return
     */
    private BeanDefinitionHolder createBeanDefinition(Class<?> fieldClass) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ProxyFactoryBean.class);
        String className = fieldClass.getName();
        // bean name     ,spring      
        String beanName = StringUtils.uncapitalize(className.substring(className.lastIndexOf('.')+1));
        //  ProxyFactoryBean    
        builder.addPropertyValue("interfaceClass", fieldClass);
        builder.addPropertyValue("client", client);
        return new BeanDefinitionHolder(builder.getBeanDefinition(), beanName);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

ここではプロキシ類に対する要求は同じで、委託類の類名を取得し、要求方法名、要求パラメータ情報などを遠端に送りますので、ここではFactoryBeanを利用して統一したプロキシ類に戻ります.
/**
 * InitializingBean:    afterPropertiesSet   ,  interfaceClass        
 *     ProxyFactoryBeanRegistry#createBeanDefinition      
 *    BeanDefination,  interfaceClass,client     
 */
@Slf4j
public class ProxyFactoryBean implements FactoryBean<Object>, InitializingBean {
    private Client client;
    private Class<?> interfaceClass; //          
    private Object proxy;

    @Override
    public Object getObject() throws Exception {
        return proxy;
    }

    @Override
    public Class<?> getObjectType() {
        return interfaceClass;
    }

    @Override
    public boolean isSingleton() {
        return true; //   
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.proxy = Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                (proxy, method, args) -> {
                    ......
                });
    }

    //     setxxx           
    public void setClient(RpcClient client) {
        this.client = client;
    }

    public void setInterfaceClass(Class<?> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }
}
次はプロジェクトコードの中に次のように直接注ぎ込むだけでいいです.
    @Autowired
    @MyReference
    HelloService helloService;
helloService皰xxxメソッドの呼び出しは直接代理クラスをトリガします.