背景
最近读了一下 spring cloud 的 @RefreshScope 生效的源码,总结一下该注解的 refresh 类型的类实例化的过程。
关键技术点:
- 扫描过程中对
@RefreshScope注解做了特殊处理,会额外注册两个BeanDefinition。 GenericScope实现了BeanDefinitionRegistryPostProcessor接口,并对 refresh 的BeanDefinition添加了构造函数参数为自己,同时设置 beanClass 属性为GenericScope.LockedScopedProxyFactoryBean.class,合成属性为true。GenericScope.LockedScopedProxyFactoryBean类实现了FactoryBean、BeanFactoryAware、MethodInterceptor接口。
拆解三个部分:扫描 Bean 定义、代理类实例化、代理类方法调用,从这三个过程的源码,来理解为什么 @RefreshScope 标注的类的实能够动态应对环境变量的变化。
原始扫描 Bean 定义过程
ClassPathBeanDefinitionScanner 扫描时会对 @RefreshScope 注解的类的 BeanDefinition 定义信息作两个操作:

这个 applyScopedProxyMode 方法判断当前代理模式,如果

而 @RefreshScope 的这个属性是 ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS ,即针对这类注解的类在扫描时,会额外创建代理类的 BeanDefinition :

主要是三个操作:
- 以原始的
BeanDefinition新建一个代理类的RootBeanDefinition,并且设置它的decoratedDefinition为原始对象,代理类的类型为ScopedProxyFactoryBean; - 再注册一个以
"scopedTarget." + originalBeanName,基于原始配置的目标定义,即真正实体的定义修改了; - 最后再注册一个用
originalBeanName的代理类的定义,就是偷梁换柱,完全代理了目标对象。 - 原始类定义的
autowireCandidate为 false ,primary也为 false ,那么就能保证实力获取时优先找到的候选对象是代理对象。
GenericScope 增强注册
GenericScope 实现了 BeanDefinitionRegistryPostProcessor 接口,而且它 Order 优先级较低,保证在 第一步的扫描之后执行如下操作:

这里对扫描阶段创建的代理类的 BeanDefinition 作了三个增强:
- 修改代理的
BeanDefinition类型为自己的LockedScopedProxyFactoryBean; - 为代理类的
BeanDefinition添加一个构造函数的参数值为自己,因为LockedScopedProxyFactoryBean类的构造函数需要一个入参Scope; - 设置合成属性为真。
启示录
@RefreshScope 是 spring cloud 的技术,它本质上扩展了 spring 框架的 Scope,跟其他单例、原型、请求类型、会话类型一样,是一种扩展 Scope ,巧妙应用了 ScopedProxyMode.TARGET_CLASS 和 AOP ,把 refresh 类型的对象托管起来,保证环境变量变更时,销毁旧的实例、获取最新的实例。
下一篇讲继续总结 ScopedProxyFactoryBean 这个类是如何创建出 @RefreshScope 注解的实例的,为什么能将一个委托类型偷梁换柱为一个 JdkDynamicAopProxy 代理类。
















