本文小编为大家详细介绍“@RereshScope刷新的原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“@RereshScope刷新的原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
在配合配置中心修改配置让应用自动刷新配置时,我们要在需要感知配置变化的bean上面加上@RereshScope
。如果我们不加上这注解,那么有可能无法完成配置自动刷新。
一、入口
可以看到@RereshScope
是@Scope("refresh")
(bean的作用域)的派生注解并指定了作用域为refresh
并在默认情况下proxyMode= ScopedProxyMode.TARGET_CLASS
即使用CGLIB生成代理对象。
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Scope("refresh")@Documentedpublic @interface RefreshScope { ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}
ScopedProxyMode
ScopedProxyMode
表示作用域的代理模式,共有以下四个值:
DEFAULT
:默认noNO
:不使用代理INTERFACES
:使用JDK动态代理TARGET_CLASS
:使用CGLIB动态代理
public enum ScopedProxyMode { DEFAULT, NO, INTERFACES, TARGET_CLASS;}
二、配置类解析
在上文刷新时会执行BeanFacotryPostProcessor
对beanDefinition
修改和增加,其中配置类解析、类扫描的工作就是在其中执行,而对于一个ScopedProxyMode.NO
它会解析成一个ScopedProxyFactoryBean
//ConfigurationClassBeanDefinitionReader配置类解析代码片段definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);//如果需要生产代理则创建一个ScopedProxy的BeanDefinitionstatic BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();if (scopedProxyMode.equals(ScopedProxyMode.NO)) {return definition;}boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);}//createScopedProxy 代码片段 可以看到BeanDefinition是ScopedProxyFactoryBeanRootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
ScopedProxyFactoryBean-生成代理对象
FactoryBean
在注入时返回的是getObject()
所返回的对象,在这里就是返回的就是proxy
。ScopedProxyFactoryBean
实现了BeanFactoryAware
那么在这个bean初始化中会调用setBeanFactory()
方法,而在这个方法中,为它创建一个CGLIB
代理对象作为getObject()
的返回值,并使用ScopedObject
来代替被代理对象。而在ScopedObject
默认实现中每次都是从BeanFactory
中获取(重点)。
@Overridepublic Object getObject() {if (this.proxy == null) {throw new FactoryBeanNotInitializedException();} //返回代理对象return this.proxy;}@Overridepublic void setBeanFactory(BeanFactory beanFactory) { //...省略其他代码 // Add an introduction that implements only the methods on ScopedObject. //增加一个拦截使用ScopedObject来被代理对象调用方法 ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName()); //委托ScopedObject去执行 pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject)); // Add the AopInfrastructureBean marker to indicate that the scoped proxy // itself is not subject to auto-proxying! Only its target bean is. AOP时复用这个代理对象 pf.addInterface(AopInfrastructureBean.class); //创建代理对象 this.proxy = pf.getProxy(cbf.getBeanClassLoader());}
ScopedObject-从容器中获取代理目标
作用域对象的AOP
引入的接口。可以将从ScopedProxyFactoryBean
创建的对象强制转换到此接口,从而可以控制访问原始目标对象并通过编程删除目标对象。在默认实现中是每次方法拦截都从容器中获取被代理的目标对象。
public interface ScopedObject extends RawTargetAccess { //返回当前代理对象后面的目标对象 Object getTargetObject(); void removeFromScope();}public class DefaultScopedObject implements ScopedObject, Serializable { //...省略字段信息和构造器@Overridepublic Object getTargetObject() { //从容器中获取return this.beanFactory.getBean(this.targetBeanName);}@Overridepublic void removeFromScope() {this.beanFactory.destroyScopedBean(this.targetBeanName);}}
三、作用域原理
在BeanFactory
获取bean时(doGetBean
),如果**不是单例或者原型bean
**将交给对应的Socpe
去bean
,而创建bean
方式和单例bean是一样的。其他作用域像request
、session
等等都是属于这一块的扩展:SPI+策略模式
//AbstractBeanFactory doGetBean()代码片段String scopeName = mbd.getScope();//获取对应的scopefinal Scope scope = this.scopes.get(scopeName);//参数检查省略。。。try { //使用的对应的Socpe去获取bean 获取不到则使用后面的`ObjectFactory` Object scopedInstance = scope.get(beanName, () -> { //ObjectFactory lambda表达式 怎么创建bean beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
RefreshScope
继承GenericScope
每次获取bean是从自己的缓存(ConcurrentHashMap
)中获取。 如果缓存中bean被销毁了则用objectFactory
创建一个。
//GenericScope 中获取get实现public Object get(String name, ObjectFactory<?> objectFactory) { //从缓存中获取 缓存的实现就是ConcurrentHashMap BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory)); this.locks.putIfAbsent(name, new ReentrantReadWriteLock()); try { return value.getBean(); } catch (RuntimeException var5) { this.errors.put(name, var5); throw var5; }}
private static class BeanLifecycleWrapper { //当前bean对象 private Object bean; //销毁回调 private Runnable callback; //bean名称 private final String name; //bean工厂 private final ObjectFactory<?> objectFactory; //获取 public Object getBean() { if (this.bean == null) { synchronized(this.name) { if (this.bean == null) { this.bean = this.objectFactory.getObject(); } } } return this.bean; } //销毁 public void destroy() { if (this.callback != null) { synchronized(this.name) { Runnable callback = this.callback; if (callback != null) { callback.run(); } this.callback = null; //只为null this.bean = null; } } }}
四、配置刷新
当配置中心刷新配置之后,有两种方式可以动态刷新Bean的配置变量值,(SpringCloud-Bus
还是Nacos
差不多都是这么实现的):
向上下文发布一个
RefreshEvent
事件Http
访问/refresh
这个EndPoint
不管是什么方式,最终都会调用ContextRefresher
这个类的refresh
方法
public synchronized Set<String> refresh() { //刷新环境 Set<String> keys = this.refreshEnvironment(); //刷新bean 其实就是销毁refreshScope中缓存的bean this.scope.refreshAll(); return keys;}//RefreshScope刷新public void refreshAll() { super.destroy(); this.context.publishEvent(new RefreshScopeRefreshedEvent());}
读到这里,这篇“@RereshScope刷新的原理是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。