文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

@RereshScope刷新的原理是什么

2023-07-04 17:45

关注

本文小编为大家详细介绍“@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表示作用域的代理模式,共有以下四个值:

public enum ScopedProxyMode {      DEFAULT,      NO,      INTERFACES,      TARGET_CLASS;}

二、配置类解析

在上文刷新时会执行BeanFacotryPostProcessorbeanDefinition修改和增加,其中配置类解析、类扫描的工作就是在其中执行,而对于一个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()所返回的对象,在这里就是返回的就是proxyScopedProxyFactoryBean实现了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**将交给对应的Socpebean,而创建bean方式和单例bean是一样的。其他作用域像requestsession等等都是属于这一块的扩展: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差不多都是这么实现的):

不管是什么方式,最终都会调用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刷新的原理是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯