likes
comments
collection
share

Spring Cloud @RefreshScope 原理分析:扫描 Bean 定义

作者站长头像
站长
· 阅读数 17

背景

最近读了一下 spring cloud 的 @RefreshScope 生效的源码,总结一下该注解的 refresh 类型的类实例化的过程。

关键技术点:

  1. 扫描过程中对 @RefreshScope 注解做了特殊处理,会额外注册两个BeanDefinition
  2. GenericScope 实现了 BeanDefinitionRegistryPostProcessor 接口,并对 refresh 的 BeanDefinition 添加了构造函数参数为自己,同时设置 beanClass 属性为 GenericScope.LockedScopedProxyFactoryBean.class,合成属性为 true
  3. GenericScope.LockedScopedProxyFactoryBean 类实现了 FactoryBeanBeanFactoryAwareMethodInterceptor 接口。

拆解三个部分:扫描 Bean 定义、代理类实例化、代理类方法调用,从这三个过程的源码,来理解为什么 @RefreshScope 标注的类的实能够动态应对环境变量的变化。

原始扫描 Bean 定义过程

ClassPathBeanDefinitionScanner 扫描时会对 @RefreshScope 注解的类的 BeanDefinition 定义信息作两个操作: Spring Cloud @RefreshScope 原理分析:扫描 Bean 定义 这个 applyScopedProxyMode 方法判断当前代理模式,如果 Spring Cloud @RefreshScope 原理分析:扫描 Bean 定义@RefreshScope 的这个属性是 ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS ,即针对这类注解的类在扫描时,会额外创建代理类的 BeanDefinitionSpring Cloud @RefreshScope 原理分析:扫描 Bean 定义 主要是三个操作:

  1. 以原始的 BeanDefinition 新建一个代理类的 RootBeanDefinition,并且设置它的 decoratedDefinition 为原始对象,代理类的类型为 ScopedProxyFactoryBean
  2. 再注册一个以 "scopedTarget." + originalBeanName ,基于原始配置的目标定义,即真正实体的定义修改了;
  3. 最后再注册一个用 originalBeanName 的代理类的定义,就是偷梁换柱,完全代理了目标对象。
  4. 原始类定义的 autowireCandidate 为 false ,primary 也为 false ,那么就能保证实力获取时优先找到的候选对象是代理对象。

GenericScope 增强注册

GenericScope 实现了 BeanDefinitionRegistryPostProcessor 接口,而且它 Order 优先级较低,保证在 第一步的扫描之后执行如下操作: Spring Cloud @RefreshScope 原理分析:扫描 Bean 定义 这里对扫描阶段创建的代理类的 BeanDefinition 作了三个增强:

  1. 修改代理的 BeanDefinition 类型为自己的 LockedScopedProxyFactoryBean
  2. 为代理类的 BeanDefinition 添加一个构造函数的参数值为自己,因为 LockedScopedProxyFactoryBean 类的构造函数需要一个入参 Scope
  3. 设置合成属性为真。

启示录

@RefreshScope 是 spring cloud 的技术,它本质上扩展了 spring 框架的 Scope,跟其他单例、原型、请求类型、会话类型一样,是一种扩展 Scope ,巧妙应用了 ScopedProxyMode.TARGET_CLASS 和 AOP ,把 refresh 类型的对象托管起来,保证环境变量变更时,销毁旧的实例、获取最新的实例。

下一篇讲继续总结 ScopedProxyFactoryBean 这个类是如何创建出 @RefreshScope 注解的实例的,为什么能将一个委托类型偷梁换柱为一个 JdkDynamicAopProxy 代理类。