实现这样一种功能:
自定义了一个注解@MyReference,
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyReference{
}
被它注解的字段是就是我们要代理的类,希望在Spring启动时将代理类注入到这些被该注解标识的字段。
如何实现:
1,如何实现在Spring启动后将被@MyReference
标注的类的替换 ?
将代理类的beanDefination注册到容器中,并将其与委托类在容器中的name关联,这样容器注入的就是我们的代理类了。
利用 BeanDefinitionRegistryPostProcessor
来实现。
这里我们代理类的功能是一样的,都是获取委托类的信息来向远端发送请求再获得结果,所以利用FactoryBean
与InitializingBean
,创建一个叫 ProxyFactoryBean 的类实现它们,内部声明一个成员变量用来标识委托类的类型,afterPropertiesSet方法中根据类型创建对应代理类,该方法在初始化bean时被调用;对于每个标注类都会创建一个 ProxyFactoryBean 类型的beanDefination。
这样我们项目下所有被标注的类,其在Spring容器中的BeanDefination实质上是个FactoryBean,而Spring在获取一个Bean实例时若发现其是FactoryBean,则调用它的getObject,由于我们实现了InitializingBean,在bean初始化时afterPropertiesSet被调用,我们在该方法里根据类型创建该标注类的代理类对象,getObject返回的也是它。具体代码实现在下面。
2,我们使用的是SpringBoot,上述功能作为一个独立的模块,在项目中进行依赖,需要在容器启动时进行触发,如何触发?利用Spring Boot的Spring Factories机制来实现。
关于Spring Factories推荐两篇文章:
Spring Boot的扩展机制之Spring Factories
EnableAutoConfiguration注解的工作原理
功能模块的resources目录下 META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.miao.client.MyAutoConfiguration
MyAutoConfiguration
@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