@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. vmstat参数详解

    vmstat 5 可以使用ctrl+c停止vmstat,可以看到输出依赖于所用的操作系统,因此可能需要阅读一下手册来解读报告 第一行的值是显示子系统启动以来的平均值,第二行开始展示现在正在发生的情况, ...

  2. show engine innodb status

    TRANSACTIONS------------Trx id counter 2003909(当前事务号)Purge done for trx's n:o < 2003905 (清理线程完成到了 ...

  3. P1273 有线电视网(树形动规,分组背包)

    题目链接: https://www.luogu.org/problemnew/show/P1273 题目描述 某收费有线电视网计划转播一场重要的足球比赛.他们的转播网和用户终端构成一棵树状结构,这棵树 ...

  4. 两行代码修复了解析MySQL8.x binlog错位的问题!!

    写在前面 MySQL是互联网行业使用的最多的关系型数据库之一,而且MySQL又是开源的,对于MySQL的深入研究,能够加深我们对于数据库原理的理解.自从开源了mykit-data之后,不少小伙伴试用后 ...

  5. Jenkins自动部署spring boot

    Jenkins自动部署spring boot 背景介绍 本公司属于微小型企业,初期业务量不高,所有程序都写在一个maven项目里面,不过是多模块开发. 分了login模块,service模块,cms模 ...

  6. wmic 操作文件的datafile

    wmic datafile /?动词有ASSOC,CALL,CREATE,DELETE,GET,LIST 这几个 命令:wmic datafile where "filename='dsc0 ...

  7. USB2514集线器调试总结

    一般的MCU不会留有太多的USB口,但在实际项目中又会遇到需要很多个USB口的情况,这时就会用到USB集线器来扩展USB口了.USB2514这个芯片是我在工作中用的比较多的,但是问题很多,从来没有调稳 ...

  8. 免安装的tomcat转服务

    一:确保tomcat 在点击bin\startup 文件可以正常启动访问: 二:本机安装有JDK: 三:本机环境变量配置:JAVA_HOME:C:\Java\jdk1.7.0_17; 四:本机Tomc ...

  9. 编码占用的字节数 1 byte 8 bit 1 sh 1 bit 中文字符编码 2. 字符与编码在程序中的实现 变长编码 Unicode UTF-8 转换 在网络上传输 保存到磁盘上 bytes

    小结: 1.UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等: 2 服务器->网页 utf-8 ...

  10. ip_hash(不推荐使用) 会话粘性问题分析 Cookie 的 Session Sticky

    Nignx 连接tomcat时会话粘性问题分析_changyanmanman的专栏-CSDN博客_后端tomcat导致 前端elb中断 https://blog.csdn.net/cymm_liu/a ...