@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[设置域,以及代理类型]
    image
  • org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy[创建域代理]
    image
  • 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. 感谢阅读!欢迎大家提出意见建议

版权声明:本文为MyNameIsSam原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/MyNameIsSam/p/14393078.html