@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. 感谢阅读!欢迎大家提出意见建议

最新文章

  1. div滚动条弹出层效果 (所需要的css文件和js文件,都已经上传到文件里面了progressbar.rar)
  2. Hadoop入门进阶课程7--Pig介绍、安装与应用案例
  3. java web 学习十五(jsp基础语法)
  4. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(17)-LinQ动态排序
  5. Codeforces 474F - Ant colony
  6. Hybrid App开发模式中, IOS/Android 和 JavaScript相互调用方式
  7. nginx反向代理与负载均衡
  8. git----------如何安装gitlab,使用步骤。
  9. .Net与 WebAssembly 随笔
  10. winform Combobox出现System.Data.DataRowView的解决的方法
  11. Webpack学习-工作原理(上)
  12. python3学习笔记13(数据结构)
  13. window django-https 证书
  14. Git简介及安装
  15. 解决mysql:Can&#39;t connect to local MySQL server through socket &#39;/var/lib/mysql/mysql.sock&#39; (111)
  16. Java基础(变量数&amp;常量&amp;据类型&amp;类型转换)
  17. C++之new、delete 与malloc、free的异同
  18. POJ2279杨氏矩阵+钩子定理
  19. [翻译]CSS3 Media Queries
  20. C# 灵活用法拾遗

热门文章

  1. 利用Numpy求解投资内部收益率IRR
  2. Leetcode53. 最大子序列和
  3. ctfhub技能树—sql注入—UA注入
  4. linux下删除文件夹及下面所有文件
  5. django之orm单表查询
  6. ryu—交换机
  7. 遍历仓库里的 commit log 替换author
  8. 每天学一点 Vue3(一) CND方式的安装以及简单使用
  9. http://golang.org/s/better-linker
  10. CSS补充2