SpringBoot之@RefreshScope

news2024/11/18 16:54:29

注解RefreshScope时一个组合注解。

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
	// Scope代理模式之ScopedProxyMode,包含TARGET_CLASS、INTERFACES、DEFAULT、NO
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}

1.ClassPathBeanDefinitionScanner

在这里插入图片描述
在这里插入图片描述

public class ClassPathBeanDefinitionScanner{
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {//应用启动类所在的包路径
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);// 获取到全部的候选类
			for (BeanDefinition candidate : candidates) {
				// 解析当前类存在Scope注解
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				//设置Scope的范围:单例singleton、原型prototype、refresh。对于@RefreshScope分析其Scope 为 refresh
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
			//此处针对RefreshScope注解的bean,生成两个BeanDefinition注册在DefaultListableBeanFactory#beanDefinitionMap属性中。
			//其中之一:bean为 scopedTarget.xxx,BeanDefinition中beanClass属性为目标类信息。
			//其中之一:bean位xxx ,其中BeanDefinition中beanClass属性为通过ScopedProxyMode设置为ScopedProxyFactoryBean。
			// ScopedProxyFactoryBean 为FactoryBean
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}

后续解析目标bean时其类信息为ScopedProxyFactoryBean。但其实在解析RefreshScope中最终将其设置为LockedScopedProxyFactoryBean,具体参考以下章节。

2.RefreshScope

当前类是通过自动装配候选类RefreshAutoConfiguration触发的。
在这里插入图片描述
通过上述UML图得知其几点重要信息:

  1. ApplicationContextAware表明存在应用上下文变量。
  2. 实现了Ordered接口的BeanDefinitionRegistryPostProcessor类型的后置处理器。【实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor后置处理器着重处理所有bean的BeanDefinition】。
  3. 实现了BeanFactoryPostProcessor类型的后置处理器。
  4. 监听ContextRefreshedEvent事件的ApplicationListener类型的监听器。
public class RefreshScope{	
	// BeanDefinitionRegistryPostProcessor类型的后置处理器 核心回调方法
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
			throws BeansException {
		this.registry = registry;
		super.postProcessBeanDefinitionRegistry(registry);//GenericScope#postProcessBeanDefinitionRegistry
	}
}
public class GenericScope{
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){
		// 遍历全部的BeanDefinition,BeanDefinition是后续实例化bean需要的类信息
		for (String name : registry.getBeanDefinitionNames()) {
			BeanDefinition definition = registry.getBeanDefinition(name);
			if (definition instanceof RootBeanDefinition) {
				RootBeanDefinition root = (RootBeanDefinition) definition;
				if (root.getDecoratedDefinition() != null && root.hasBeanClass()
						&& root.getBeanClass() == ScopedProxyFactoryBean.class) {// 处理ScopedProxyFactoryBean类型的bean
					if (getName().equals(root.getDecoratedDefinition().getBeanDefinition().getScope())) {
						// 将 ScopedProxyFactoryBean类型的bean 再次设置为 LockedScopedProxyFactoryBean类型的bean
						root.setBeanClass(LockedScopedProxyFactoryBean.class);
						root.getConstructorArgumentValues().addGenericArgumentValue(this);
						root.setSynthetic(true);
					}
				}
			}
		}
	}
}

通过上述得知,在DefaultListableBeanFactory#beanDefinitionMap中存在两种类型目标类相关的BeanDefinition。那是不是意味着在IOC容器中也存在目标类相关的两种的bean呢?

事实是scopedTarget.xxx类型的BeanDefinition其scope为refresh并非单例所以不会实例化。xxx类型在IOC容器中的单例为LockedScopedProxyFactoryBean。虽然目标类最终存在代理,但是在IOC容器中并非是目标类的代理类。

3.LockedScopedProxyFactoryBean

在这里插入图片描述
通过上面章节得知,存在@RefreshScope的bean其BeanDefinition中Class属性为FactoryBean类型的class,即LockedScopedProxyFactoryBean。

  1. LockedScopedProxyFactoryBean实现了BeanFactoryAware接口。
  2. 实现MethodInterceptor接口实现对目标bean做Cglib代理处理。
public static class LockedScopedProxyFactoryBean<S extends GenericScope>
		extends ScopedProxyFactoryBean implements MethodInterceptor {

	private final S scope;

	private String targetBeanName;//scopedTarget.xxx

	public LockedScopedProxyFactoryBean(S scope) {
		this.scope = scope;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		// ScopedProxyFactoryBean#setBeanFactory 对目标bean做Cglib代理
		super.setBeanFactory(beanFactory);
		Object proxy = getObject();
		if (proxy instanceof Advised) {
			Advised advised = (Advised) proxy;
			advised.addAdvice(0, this);
		}
	}

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Method method = invocation.getMethod();
		if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
				|| AopUtils.isHashCodeMethod(method)
				|| isScopedObjectGetTargetObject(method)) {
			return invocation.proceed();
		}
		Object proxy = getObject();
		ReadWriteLock readWriteLock = this.scope.getLock(this.targetBeanName);
		if (readWriteLock == null) {
			readWriteLock = new ReentrantReadWriteLock();
		}
		Lock lock = readWriteLock.readLock();
		lock.lock();
		if (proxy instanceof Advised) {
			Advised advised = (Advised) proxy;
			ReflectionUtils.makeAccessible(method);
			return ReflectionUtils.invokeMethod(method,
					advised.getTargetSource().getTarget(),
					invocation.getArguments());
		}
		return invocation.proceed();
	}

}

3.1.ScopedProxyFactoryBean

public class ScopedProxyFactoryBean extends ProxyConfig
		implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {
	private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
	@Nullable
	private String targetBeanName;
	@Nullable
	private Object proxy;
	public ScopedProxyFactoryBean() {
		setProxyTargetClass(true);
	}
	public void setTargetBeanName(String targetBeanName) {
		this.targetBeanName = targetBeanName;
		this.scopedTargetSource.setTargetBeanName(targetBeanName);
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
		this.scopedTargetSource.setBeanFactory(beanFactory);
		ProxyFactory pf = new ProxyFactory();
		pf.copyFrom(this);
		// 目标类创建代理,注意是对scopedTargetSource对应的class
		pf.setTargetSource(this.scopedTargetSource);
		// 获取目标类的Class对象
		Class<?> beanType = beanFactory.getType(this.targetBeanName);
		if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
			pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
		}
		ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
		pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
		pf.addInterface(AopInfrastructureBean.class);
		this.proxy = pf.getProxy(cbf.getBeanClassLoader());
	}


	@Override
	public Object getObject() {
		if (this.proxy == null) {
			throw new FactoryBeanNotInitializedException();
		}
		return this.proxy;
	}
}

如上所示,对目标类实例化过程中由于其BeanDefinition的class为FactoryBean类型的属性值,即存在代理过程。但是代理对象并非存在于IOC容器中,而是通过FactoryBean#getObject实时获取。

4.真正的代理过程

解析HTTP请求过程中,存在通过beanName获取handler过程,如下:

public abstract class AbstractBeanFactory{
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		final String beanName = transformedBeanName(name);
		Object bean;
		//此时根据beanName从IOC容器中获取的是LockedScopedProxyFactoryBean
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}else {// 如果获取scopedTarget.xxx对应的bean实例信息
			BeanFactory parentBeanFactory = getParentBeanFactory();
			...
			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);
			...
			if (mbd.isSingleton()) {//scope表示为单例
				...
			}else if (mbd.isPrototype()) {//scope表示为Prototype
				...
			}else {//scope表示为refresh
				String scopeName = mbd.getScope();
				final Scope scope = this.scopes.get(scopeName);//获取到RefreshScope
				final Scope scope = this.scopes.get(scopeName);
				// 从RefreshScope中获取scopedTarget.xxx对应的bean实例信息,存在则返回,否则通过Lambda表达式新建实例信息
				// scope#get涉及目标类的缓存信息,如果存在缓存表明目标类对应的配置信息没有发生变化。
				// 没有缓存则新建目标类的实例信息,实例化过程也就是对其属性重新赋值的过程,即最新的value值,实现了配置信息动态刷新的功能
				Object scopedInstance = scope.get(beanName, () -> {
					beforePrototypeCreation(beanName);
					try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}
				});
				//scopedInstance 即目标类的实例信息,此时scopedTarget.xxx获取到的就是普通的bean
				bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
			}
		}
		...
		return (T) bean;
	}
	
	protected Object getObjectForBeanInstance(Object beanInstance,String name,String beanName,RootBeanDefinition mbd) {
		//如果beanInstance 不是 FactoryBean 则直接返回
		if (!(beanInstance instanceof FactoryBean)) {
			return beanInstance;
		}
		// 此处就是通过 FactoryBean#getObject获取到目标类的代理对象
		Object object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		...
		return object;
	}
}

通过上述得知目标类最终得到其Cglib代理的对象,执行目标方法时被如下方法拦截:

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

	private final AdvisedSupport advised;

	public DynamicAdvisedInterceptor(AdvisedSupport advised) {
		this.advised = advised;
	}

	@Override
	@Nullable
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;
		Object target = null;
		//advised.getTargetSource:从ProxyFactory中获取targetSource,即scopedTarget.xxx对应的类信息
		TargetSource targetSource = this.advised.getTargetSource();
		...
		// 获取TargetSource实例中目标source即scopedTarget.xxx对应的bean实例信息。继续调用上述AbstractBeanFactory#doGetBean方法
		// 涉及本文中核心逻辑之配置信息动态刷新
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		Object retVal;
		retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, 
																	methodProxy).proceed();// 最终执行目标类的目标方法
		retVal = processReturnType(proxy, target, method, retVal);
		return retVal;
	}
}

综上所述得知配置信息动态刷新的过程。还缺少一环即配置更新同时删除目标类实例在GenericScope中缓存值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

技术领导力实战笔记:14

14&#xff5c;团队优化&#xff1a;如何妥善且优雅地做好解聘工作&#xff1f; 我们需要在思想上对这件事情有一个正确的认识&#xff0c;解聘对团队、个人和管理者三方都是有好处的。摆正自己的立场才能做出正确的决定。 1.公司发展太快&#xff0c;个人没有跟上脚步 2.个人…

Go在安装Gin时出现Failed to connect 报错问题的解决方案(已解决)

在命令行中输入&#xff1a;go get -u github.com/gin-gonic/gin指令安装Gin第三方包时出现连接错误与连接超时的情况如下&#xff1a; 在较新版本的Go中引入了全新的包管理机制&#xff0c;出现上述错误可能是包管理机制设置不恰当的问题&#xff0c;尝试在终端窗口输入如下…

Mysql数据库(1)—索引

索引是什么&#xff1f; 索引是帮助MySQL高效获取数据的排好序的数据结构。常见的索引数据结构包括&#xff1a; 二叉树红黑树Hash表B-Tree mysql索引分类 按逻辑结构分类&#xff1a;B tree索引、Hash索引、Full-text索引。按物理存储分类&#xff1a; &#xff08;1&…

更健康舒适更科技的照明体验!SUKER书客护眼台灯 L1上手体验

低价又好用的护眼台灯是多数人的需求&#xff0c;很多人只追求功能性护眼台灯&#xff0c;显色高、无频闪、无蓝光等基础需求。但是在较低价格中很难面面俱到&#xff0c;然而刚发布的SUKER书客L1护眼台灯却是一款不可多得的性价比护眼台灯&#xff0c;拥有高品质光源&#xff…

自我管理篇--想要快速融入新公司,你需要用些小妙招呢

自我管理篇—想要快速融入新公司&#xff0c;你需要用些小妙招呢&#xff01; 文章目录 一、建立良好的人际关系二、了解公司文化和价值观三、展现自己的能力和价值四、适应和融入团队五、建立良好的工作关系六、塑造专业的职业形象七、提升沟通和协调能力八、关注公司的发展动…

【数值计算方法】导论

目录 一、极简数学史 1. 萌芽时期 2. 古典数学时期 3. 近代前期 4. 近代后期 5. 现代数学 二&#xff0c;计算方法学什么&#xff1f; 1. 数值代数 a. 线性代数方程组求解&#xff08;等价变换&#xff09; b. 矩阵特征值特征向量&#xff08;相似变换&#xff09; …

【Android Framework系列】第13章 SVG矢量图形自定义组件(绘制中国地图)

1 前言 本章节我们来了解下什么是SVG矢量图形&#xff0c;怎么通过SVG实现图形的绘制&#xff0c;通过SVG实现不规则的自定义控件&#xff0c;项目实现一个中国地图&#xff0c;实现每个省都能够点击&#xff0c;项目地址在文末请自取。 2 SVG概念 2.1 SVG矢量图形 SVG 指可…

【机器视觉】HALCON目标图像检测实践(零基础版)

开发环境&#xff1a; 基本思路&#xff1a; 灰度阈值处理特征提取 ps:思考&#xff1a;如何通过阈值处理后&#xff0c;后续缩小检测目标范围&#xff1f; 二值化、梯度阈值、自适应阈值等方法来增强图像的对比度&#xff0c;从而突出目标特征&#xff0c;进一步缩小检测目标…

净利同比大增158%,国民内衣都市丽人已显“飞轮效应”

作者 | 曾响铃 文 | 响铃说 自2021年底创始人郑耀南回归开启“二次创业”后&#xff0c;都市丽人变革效果显著&#xff0c;稳健经营之路“渐入佳境”&#xff0c;好消息不断。 最新的半年财报&#xff0c;更是延续了这一点。最新财报显示&#xff0c;2023年上半年&#xff0…

基于AI智能分析网关EasyCVR视频汇聚平台关于能源行业一体化监控平台可实施应用方案

随着数字经济时代的到来&#xff0c;实体经济和数字技术深度融合已成为经济发展的主流思路。传统能源行业在运营管理方面也迎来了新的考验和机遇。许多大型能源企业已开始抓住机遇&#xff0c;逐步将视频监控、云计算、大数据和人工智能技术广泛应用于生产、维护、运输、配送等…

8.31 加载资源文件 信号与槽机制

登录窗口 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this -> setFixedSize(540, 410); //固定窗口大小this -> setWindowTitle("啊啊啊"…

比较opencv,pillow,matplotlib,skimage读取图像的速度比

上面这些库都被广泛用于图像处理和计算机视觉任务&#xff1b; 不同的图像读取库&#xff08;OpenCV&#xff0c;Pillow&#xff0c;matplotlib和skimage&#xff09;的读取速度&#xff0c;是怎么样的一个情况&#xff1f; 下面分别从读取速度&#xff0c;以及转换到RGB通道…

文心一言向全社会开放

大家好&#xff0c;我是洋子 今天和大家宣传一件令人激动的事情 8月31日&#xff0c;文心一言率先向全社会全面开放。广大用户可以在应用商店下载“文心一言APP”或登陆“文心一言官网(https://yiyan.baidu.com)体验。同时&#xff0c;企业用户可以直接登陆百度智能云千帆大模…

云渲染平台是如何收费的?

现在的云渲染平台实在太多了&#xff0c;那么多平台&#xff0c;他们都是如何收费的呢&#xff1f;这篇文章我们就一块来看看吧。 一、渲染100(http://www.xuanran100.com?ycode1a12) 渲染100位于成都&#xff0c;是国内知名的云渲染公司&#xff0c;15分钟2毛60分钟8毛非常适…

《Flink学习笔记》——第十二章 Flink CEP

12.1 基本概念 12.1.1 CEP是什么 1.什么是CEP&#xff1f; 答&#xff1a;所谓 CEP&#xff0c;其实就是“复杂事件处理&#xff08;Complex Event Processing&#xff09;”的缩写&#xff1b;而 Flink CEP&#xff0c;就是 Flink 实现的一个用于复杂事件处理的库&#xff08…

Java之API详解之BigDecimal类的详细解析

7 BigDecimal类 7.1 引入 首先我们来分析一下如下程序的执行结果&#xff1a; public class BigDecimalDemo01 {public static void main(String[] args) {System.out.println(0.09 0.01);}} 这段代码比较简单&#xff0c;就是计算0.09和0.01之和&#xff0c;并且将其结果…

模拟电子技术基础学习笔记三 PN结

采用不周的掺杂工艺&#xff0c;将P型半导体与N型半导体制作在同一块硅片上&#xff0c;在它们的交界面就形成PN结。 扩散运动 物质总是从浓度高的地方向浓度低的地方运动&#xff0c;这种由于浓度差而产生的运动称为扩散运动。 空间电荷区 - 耗尽层 漂移运动 在电场力的作…

【马蹄集】第二十四周——高精度计算专题

高精度计算专题 目录 MT2191 整数大小比较MT2192 AB problemMT2193 A-B problemMT2194 大斐列MT2195 升级版斐波那契数列MT2196 2的N次幂 MT2191 整数大小比较 难度&#xff1a;黄金    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目描述 给出两个正整数&…

邮件群发的功能优势

动态IP切换登录发送 保证送达率 软件可自动切换不同的动态拨号VPS的IP&#xff0c;登录不同的第三方免费邮件发送方&#xff0c;模拟真实环境&#xff0c;一个IP登录一个小号邮件账号发送&#xff0c;这样可以绕过因为一个IP同时登录同个第三方免费邮箱的不同账户而造成的屏蔽&…

HUAWEI华为笔记本MateBook 16 2021款 锐龙版 R7 集显(CREM-WFD9)原厂Win10系统

华为原装出厂系统自带指纹、显卡、声卡、网卡等所有驱动、出厂主题壁纸LOGO、Office办公软件、华为电脑管家等预装程序 链接&#xff1a;https://pan.baidu.com/s/18XIYnasYFfLxSKS6LfoHvw?pwdly6j 提取码&#xff1a;ly6j