@RefreshScope

  1. 前言
  • 该文章是由上一篇引申而来的,上一篇文章描述了Nacos是怎么触发配置刷新的,接着来写有关@RefreshScope的一些东西,都是由DEBUG而来
  • 上一篇
  1. 文章概览
  • 执行配置中心配置刷新后,重新获取是一个什么过程,记录一些零零碎碎的DEBUG过程
  1. 实验素材
  • Nacos环境,定义一个属性类,并且被@RefreshScope注释,bean的名字为myConfig

MyConfig执行getXXX获取属性的过程

  1. 代理行为的方法代码(org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept)
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
// 这个getTarget可以调用到获取bean的方法
target = targetSource.getTarget();
// 省略N行代码
}
}
@SuppressWarnings("serial")
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource { @Override
public Object getTarget() throws Exception {
// 就触发获得bean的方法
return getBeanFactory().getBean(getTargetBeanName());
} }
  1. 获取bean方法的具体走向(org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean)
// 上面是单独判断是否是单例,原型方法,否则就到scope域
else {
String scopeName = mbd.getScope();
// 得到RefreshScope
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
// 调用创建bean的方法
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
// 如果Scope持有的BeanWrapper里的bean设置为null的话就会触发
// 否则直接返回bean
// 执行refreshEvent的时候其实就是销毁了BeanWrapper的bean
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
  1. @RefreshScope标注的bean是什么时候进入org.springframework.cloud.context.scope.GenericScope.BeanLifecycleWrapperCache的管理缓存的?
  • 尝试debug org.springframework.cloud.context.scope.GenericScope.BeanLifecycleWrapperCache#put
public BeanLifecycleWrapper put(String name, BeanLifecycleWrapper value) {
return (BeanLifecycleWrapper) this.cache.put(name, value);
}
  • spring释放上下文准备事件的时候触发org.springframework.cloud.context.scope.refresh.RefreshScope#onApplicationEvent
// 初始化
private void eagerlyInitialize() {
for (String name : this.context.getBeanDefinitionNames()) {
BeanDefinition definition = this.registry.getBeanDefinition(name);
// 判断该bean定义是否具备refresh的scope
if (this.getName().equals(definition.getScope())
&& !definition.isLazyInit()) {
Object bean = this.context.getBean(name);
if (bean != null) {
bean.getClass();
}
}
}
}
// 再一次走到get方法
// org.springframework.cloud.context.scope.GenericScope#get
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
// get的前置先进行了put
BeanLifecycleWrapper value = this.cache.put(name,
new BeanLifecycleWrapper(name, objectFactory));
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
return value.getBean();
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}

有关@RefreshScope的BeanDefinition的变化追踪

  1. 启动扫描项目路径下类以及加载BeanDefinition
  • org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents[扫描得到候选者]
  • org.springframework.context.annotation.AnnotationScopeMetadataResolver#resolveScopeMetadata[设置域,以及代理类型]

  • org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy[创建域代理]

  • org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition[创建完域代理的BeanDefinition后首次执行register到BeanFactory里,这个时候的BeanName是scopedTarget.myConfig,注意本次注册的是目标BeanDefinition]
// Register the target bean as separate bean in the factory.
registry.registerBeanDefinition(targetBeanName, targetDefinition);
  • 调用第二次Bean注册(org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition)
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
// 此时的holder中的beanname是最原始的myConfig,BeanDefinition是新创建
// 包装过原始BeanDefinition的ProxyBeanDefinition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

创建MyConfig的过程简单记录

  1. 接着跟踪到创建bean的地方(org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons)
  • 发现beanDefinitionNames中存在scopedTarget.myConfig / myConfig
  • 首先我是定义到我的一个Controller类(实验),因为它依赖了MyConfig,所以定位到了getBean("myConfig")的地方
  • 创建过程中追踪的方法栈如下
// 1. org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 进入创建bean的方法
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} // 2. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
if (instanceWrapper == null) {
// 根据BeanDefinition创建一个Bean实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
} // 3. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
// 注意进入本方法的BeanClass是org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
} // 4. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// 可以debug这个方法,直到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 进这个方法后设置了一下构造方法缓存而已,这一步也不是很懂
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
// 经过AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 得到构造器public org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean(org.springframework.cloud.context.scope.GenericScope)
return ctors;
}
}
}
} // 5. org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor 接着实现构造器注入 // 6. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean,经过第五步后,实例对象已经有属性是Scope的实例
public static class LockedScopedProxyFactoryBean<S extends GenericScope>
extends ScopedProxyFactoryBean implements MethodInterceptor {
// 在第5步已经注入
private final S scope;
// 接着注入这个对象
private String targetBeanName;
} // 后面执行bean的依赖注入,生命周期,具体看org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
// 注意这里的方法,调用setBeanFactory的时候会进入org.springframework.cloud.context.scope.GenericScope.LockedScopedProxyFactoryBean#setBeanFactory
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
// org.springframework.cloud.context.scope.GenericScope.LockedScopedProxyFactoryBean#setBeanFactory
// org.springframework.aop.scope.ScopedProxyFactoryBean#setBeanFactory
// 在这里完成myConfig的代理对象创建
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
  1. scopeTarget.myConfig去哪里了呢?
  • 从上面的分析可以得知 BeanDefinition维护的map里有一个scopeTarget.myConfig和myConfig,但是myConfig是一个被装饰过的BeanDefinition,实际上是一个org.springframework.cloud.context.scope.GenericScope.LockedScopedProxyFactoryBean
  • 我的controller依赖了MyConfig,所以获取的时候使用beanName=myConfig去获取bean,实际上是执行了解析LockedScopedProxyFactoryBean,获得了一个代理对象
  • 总所周知,ApplicationContext在refresh时候会提前初始化bean,那么scopeTarget.myConfig的处理是如何的呢?因为它的BeanDefinition被定义为scope=“refresh”,所以不会被初始化
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
} // Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 这里scopeTarget.myConfig 3个条件都是不成立的
// 什么时候会初始化scopeTarget.myConfig呢?其实是上面提到的RefreshScoppe执行start方法的时候
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 省略N行代码
}
}
}

tip

  1. 整个debug链路追踪下来一直都是一个工厂bean(org.springframework.cloud.context.scope.GenericScope.LockedScopedProxyFactoryBean),后面真正获取bean实例对象是在

    org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean

结尾

  1. 今天写的比较潦草,可能比较难以阅读,后续会回来修正
  2. 通过今天的Debug,引出了Spring的代理行为,AOP,后续会往这方面写
  3. 感谢阅读!欢迎大家提出意见建议

SpringCloud @RefreshScope标注的Bean在生命周期中怎么样的?的更多相关文章

  1. 你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?

    聊一聊Spring是怎么将AOP应用到Bean的生命周期中的? 本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spr ...

  2. 在Spring Bean的生命周期中各方法的执行顺序

    Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下十种: 通过实现 InitializingBe ...

  3. Spring的BeanPostProcessor后置处理器与bean的生命周期

    前言 本文将把Spring在Bean的生命周期中涉及到的后置处理器一一梳理出来,并简要说一下功能,至于每个后置处理器在实际扩展中的用处,还要后续慢慢探索总结. 正文 下面一步步跟进探寻那些后置处理器们 ...

  4. Spring官网阅读(十)Spring中Bean的生命周期(下)

    文章目录 生命周期概念补充 实例化 createBean流程分析 doCreateBean流程分析 第一步:factoryBeanInstanceCache什么时候不为空? 第二步:创建对象(crea ...

  5. Spring Bean 的生命周期总结

    除了使用阶段外,Spring 将 bean 的生命周期定义为实例化.属性填充.初始化和销毁四个阶段,并为每个阶段提供了多个拓展点用于自定义 bean 的创建过程.这篇文章介绍了 bean 的生命周期和 ...

  6. IOC容器中bean的生命周期

    一.Bean的生命周期 Spring IOC容器可以管理Bean的生命周期,允许在Bean生命周期的特定点执行定制的任务. Spring IOC容器对Bean的生命周期进行管理的过程如下: (1).通 ...

  7. Spring中Bean的生命周期及其扩展点

    原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6106456.html Spring中Bean的管理是其最基本的功能,根据下面的图来了解Spr ...

  8. Spring之Bean的生命周期详解

      通过前面多个接口的介绍了解了Bean对象生命周期相关的方法,本文就将这些接口的方法串起来,来了解Bean的完整的生命周期.而介绍Bean的生命周期也是面试过程中经常会碰到的一个问题,如果不注意就跳 ...

  9. Spring 中 Bean 的生命周期

    所谓 Bean 的生命周期,就是一个 Bean 从创建到销毁,所经历的各种方法调用.大致包含下面几个方法(不是全部) Bean 的实例化,调用了构造方法. 使用 setter 方法填充属性. 一旦依赖 ...

随机推荐

  1. poj-Decoding Morse Sequences(动态规划)

    Description Before the digital age, the most common "binary" code for radio communication ...

  2. 强制删除 Terminating 状态的pod

    [root@k8s-master coredns]# kubectl get podNAME                     READY   STATUS        RESTARTS   ...

  3. ALV中的分隔条(SPLITTER_CONTROL)

    如上图,可以做成左右的分割,当然也可以做成上下的分割效果,在每个分割的容器内,显示各自的内容. 需要使用的class: cl_gui_splitter_container, cl_gui_custom ...

  4. 2020年12月18号--21号 人工智能(深度学习DeepLearning)python、TensorFlow技术实战

    深度学习DeepLearning(Python)实战培训班 时间地点: 2020 年 12 月 18 日-2020 年 12 月 21日 (第一天报到 授课三天:提前环境部署 电脑测试) 一.培训方式 ...

  5. LoadRunner监控Centos和Ubuntu资源之服务器配置

    Centos 我用的版本是Centos6.8   首先更新源以及基础操作我就不说了,直接上步骤: Step 1 安装相关程序 执行命令:yum install inetd,这一步是为了安装rstatd ...

  6. 计算机网络第7版 PDF+ 计算机网络释疑与习题解答第7版 PDF 计算机网络 课后答案

    网上全都是要钱的,要么就是第六版的,属实被恶心到了. 链接:https://pan.baidu.com/s/15jCOH6LXnQfB1RwGpNgBFg提取码:byMB

  7. Java并发包源码学习系列:阻塞队列实现之SynchronousQueue源码解析

    目录 SynchronousQueue概述 使用案例 类图结构 put与take方法 void put(E e) E take() Transfer 公平模式TransferQueue QNode t ...

  8. tp5项目部署Linux环境后无法访问解决

    一.编辑fastcgi.conf文件 vim /www/server/nginx/conf/fastcgi.conf 二.添加代码 fastcgi_param PHP_ADMIN_VALUE &quo ...

  9. MySQL、SqlServer、Oracle 三种数据库的优缺点

    MySQL.SqlServer.Oracle 三种数据库的优缺点 一.MySQL 优点: 缺点: 二.SqlServer 优点: 缺点: 三.Oracle 优点: 缺点: 一.MySQL 优点: 体积 ...

  10. #define typedef 区别

    1) #define是预处理指令,在编译预处理时进行简单的替换,不作正确性检查,不关含义是否正确照样带入,只有在编译已被展开的源程序时才会发现可能的错误并报错.例如: #define PI 3.141 ...