最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

@RereshScope刷新的原理詳解

 更新時(shí)間:2022年12月04日 16:48:16   作者:gentten  
在配合配置中心修改配置讓?xiě)?yīng)用自動(dòng)刷新配置時(shí),我們要在需要感知配置變化的bean上面加上@RereshScope。如果我們不加上這注解,那么有可能無(wú)法完成配置自動(dòng)刷新。本文就來(lái)和大家講講@RereshScope刷新的原理,需要的可以參考一下

在配合配置中心修改配置讓?xiě)?yīng)用自動(dòng)刷新配置時(shí),我們要在需要感知配置變化的bean上面加上@RereshScope。如果我們不加上這注解,那么有可能無(wú)法完成配置自動(dòng)刷新。

一、入口

可以看到@RereshScope@Scope("refresh")(bean的作用域)的派生注解并指定了作用域?yàn)?code>refresh并在默認(rèn)情況下proxyMode= ScopedProxyMode.TARGET_CLASS使用CGLIB生成代理對(duì)象。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}

ScopedProxyMode

ScopedProxyMode表示作用域的代理模式,共有以下四個(gè)值:

  • DEFAULT:默認(rèn)no
  • NO:不使用代理
  • INTERFACES:使用JDK動(dòng)態(tài)代理
  • TARGET_CLASS:使用CGLIB動(dòng)態(tài)代理
public enum ScopedProxyMode {
   /**
    * Default typically equals {@link #NO}, unless a different default
    * has been configured at the component-scan instruction level.
    */
   DEFAULT,
   /**
    * Do not create a scoped proxy.
    */
   NO,
   /**
    * Create a JDK dynamic proxy implementing <i>all</i> interfaces exposed by
    * the class of the target object.
    */
   INTERFACES,
   /**
    * Create a class-based proxy (uses CGLIB).
    */
   TARGET_CLASS;
}

二、配置類(lèi)解析

在上文刷新時(shí)會(huì)執(zhí)行BeanFacotryPostProcessor對(duì)beanDefinition修改和增加,其中配置類(lèi)解析、類(lèi)掃描的工作就是在其中執(zhí)行,而對(duì)于一個(gè)ScopedProxyMode.NO它會(huì)解析成一個(gè)ScopedProxyFactoryBean

//ConfigurationClassBeanDefinitionReader配置類(lèi)解析代碼片段
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

//如果需要生產(chǎn)代理則創(chuàng)建一個(gè)ScopedProxy的BeanDefinition
static 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是ScopedProxyFactoryBean
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);

ScopedProxyFactoryBean-生成代理對(duì)象

FactoryBean在注入時(shí)返回的是getObject()所返回的對(duì)象,在這里就是返回的就是proxy。ScopedProxyFactoryBean實(shí)現(xiàn)了BeanFactoryAware那么在這個(gè)bean初始化中會(huì)調(diào)用setBeanFactory()方法,而在這個(gè)方法中,為它創(chuàng)建一個(gè)CGLIB代理對(duì)象作為getObject()的返回值,并使用ScopedObject來(lái)代替被代理對(duì)象。而在ScopedObject默認(rèn)實(shí)現(xiàn)中每次都是從BeanFactory中獲?。ㄖ攸c(diǎn))。

@Override
public Object getObject() {
	if (this.proxy == null) {
		throw new FactoryBeanNotInitializedException();
	}
    //返回代理對(duì)象
	return this.proxy;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
   //...省略其他代碼
    
   // Add an introduction that implements only the methods on ScopedObject.
   //增加一個(gè)攔截使用ScopedObject來(lái)被代理對(duì)象調(diào)用方法
   ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
   //委托ScopedObject去執(zhí)行
   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時(shí)復(fù)用這個(gè)代理對(duì)象
   pf.addInterface(AopInfrastructureBean.class);
   //創(chuàng)建代理對(duì)象   
   this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}

ScopedObject-從容器中獲取代理目標(biāo)

作用域?qū)ο蟮?code>AOP引入的接口。可以將從ScopedProxyFactoryBean創(chuàng)建的對(duì)象強(qiáng)制轉(zhuǎn)換到此接口,從而可以控制訪問(wèn)原始目標(biāo)對(duì)象并通過(guò)編程刪除目標(biāo)對(duì)象。在默認(rèn)實(shí)現(xiàn)中是每次方法攔截都從容器中獲取被代理的目標(biāo)對(duì)象。

public interface ScopedObject extends RawTargetAccess {
  //返回當(dāng)前代理對(duì)象后面的目標(biāo)對(duì)象
   Object getTargetObject();
   void removeFromScope();
}
public class DefaultScopedObject implements ScopedObject, Serializable {
    //...省略字段信息和構(gòu)造器
	@Override
	public Object getTargetObject() {
        //從容器中獲取
		return this.beanFactory.getBean(this.targetBeanName);
	}
	@Override
	public void removeFromScope() {
		this.beanFactory.destroyScopedBean(this.targetBeanName);
	}
}

三、作用域原理

BeanFactory獲取bean時(shí)(doGetBean),如果**不是單例或者原型bean**將交給對(duì)應(yīng)的Socpebean,而創(chuàng)建bean方式和單例bean是一樣的。其他作用域像request、session等等都是屬于這一塊的擴(kuò)展:SPI+策略模式

//AbstractBeanFactory doGetBean()代碼片段
String scopeName = mbd.getScope();
//獲取對(duì)應(yīng)的scope
final Scope scope = this.scopes.get(scopeName);
//參數(shù)檢查省略。。。
try {
    //使用的對(duì)應(yīng)的Socpe去獲取bean 獲取不到則使用后面的`ObjectFactory`
   Object scopedInstance = scope.get(beanName, () -> {    
       //ObjectFactory lambda表達(dá)式 怎么創(chuàng)建bean	
      beforePrototypeCreation(beanName);
      try {
         return createBean(beanName, mbd, args);
      }
      finally {
         afterPrototypeCreation(beanName);
      }
   });
   bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);

RefreshScope繼承GenericScope每次獲取bean是從自己的緩存(ConcurrentHashMap)中獲取。 如果緩存中bean被銷(xiāo)毀了則用objectFactory創(chuàng)建一個(gè)。

//GenericScope 中獲取get實(shí)現(xiàn)
public Object get(String name, ObjectFactory<?> objectFactory) {
    //從緩存中獲取 緩存的實(shí)現(xiàn)就是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 {
        //當(dāng)前bean對(duì)象
        private Object bean;
        //銷(xiāo)毀回調(diào)
        private Runnable callback;
        //bean名稱(chēng)
        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;
        }
       //銷(xiāo)毀
        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;
                }
            }
        }
}

四、配置刷新

當(dāng)配置中心刷新配置之后,有兩種方式可以動(dòng)態(tài)刷新Bean的配置變量值,(SpringCloud-Bus還是Nacos差不多都是這么實(shí)現(xiàn)的):

  • 向上下文發(fā)布一個(gè)RefreshEvent事件
  • Http訪問(wèn)/refresh這個(gè)EndPoint

不管是什么方式,最終都會(huì)調(diào)用ContextRefresher這個(gè)類(lèi)的refresh方法

public synchronized Set<String> refresh() {
     //刷新環(huán)境
     Set<String> keys = this.refreshEnvironment();
     //刷新bean 其實(shí)就是銷(xiāo)毀refreshScope中緩存的bean
     this.scope.refreshAll();
     return keys;
}
//RefreshScope刷新
public void refreshAll() {
     super.destroy();
     this.context.publishEvent(new RefreshScopeRefreshedEvent());
}

五、總結(jié)

@RereshScope會(huì)為這個(gè)標(biāo)記的bean生成ScopedProxyFactoryBean,ScopedProxyFactoryBean生成一個(gè)代理對(duì)象使每次方法調(diào)用的目標(biāo)對(duì)象都從容器中獲取,而獲取refresh作用域的Bean是RefreshScope緩存中獲取。當(dāng)配置中心在觸發(fā)刷新時(shí)RefreshScope會(huì)刪除Socpe緩存的Bean,然后下次獲取時(shí)就會(huì)用新的Environment創(chuàng)建配置修改后的Bean,這樣就達(dá)到了配置的自動(dòng)更新。

六、問(wèn)題

為什么需要生成代理對(duì)象?

因?yàn)锽ean裝配是一次性的,假設(shè)沒(méi)有代理的情況下,在另一個(gè)bean注入這個(gè)refreshBean之后就無(wú)法改變了,就算refreshBean銷(xiāo)毀(在緩存中置為null)并后面重新生成了,但是之前引用還是老的bean,這也是為什么沒(méi)有加@RefreshScope注解而導(dǎo)致配置自動(dòng)刷新失效了。

到此這篇關(guān)于@RereshScope刷新的原理詳解的文章就介紹到這了,更多相關(guān)@RereshScope刷新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)現(xiàn)快速排序算法(Quicktsort)

    Java實(shí)現(xiàn)快速排序算法(Quicktsort)

    這篇文章主要介紹了Java實(shí)現(xiàn)快速排序算法(Quicktsort),有需要的朋友可以參考一下
    2013-12-12
  • Java實(shí)現(xiàn)PDF打印的解決方案

    Java實(shí)現(xiàn)PDF打印的解決方案

    今天小編就為大家分享一篇關(guān)于Java實(shí)現(xiàn)PDF打印的解決方案,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • 關(guān)于SpringCloud整合RabbitMQ的實(shí)例

    關(guān)于SpringCloud整合RabbitMQ的實(shí)例

    這篇文章主要介紹了關(guān)于SpringCloud整合RabbitMQ的實(shí)例,消息隊(duì)列是指利用高效可靠的消息傳遞機(jī)制進(jìn)行與平臺(tái)無(wú)關(guān)的數(shù)據(jù)交流,并基于數(shù)據(jù)通信來(lái)進(jìn)行分布式系統(tǒng)的集成,是在消息的傳輸過(guò)程中保存消息的容器,需要的朋友可以參考下
    2023-07-07
  • Java中java.sql.SQLException異常的正確解決方法(親測(cè)有效!)

    Java中java.sql.SQLException異常的正確解決方法(親測(cè)有效!)

    SQLException是在Java中處理數(shù)據(jù)庫(kù)操作過(guò)程中可能發(fā)生的異常,通常是由于底層數(shù)據(jù)庫(kù)操作錯(cuò)誤或違反了數(shù)據(jù)庫(kù)規(guī)則而引起的,下面這篇文章主要給大家介紹了關(guān)于Java中java.sql.SQLException異常的正確解決方法,需要的朋友可以參考下
    2024-01-01
  • PC 端微信掃碼注冊(cè)和登錄實(shí)例

    PC 端微信掃碼注冊(cè)和登錄實(shí)例

    這篇文章主要介紹了PC 端微信掃碼注冊(cè)和登錄實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • Spring?Native打包本地鏡像的操作方法(無(wú)需通過(guò)Graal的maven插件buildtools)

    Spring?Native打包本地鏡像的操作方法(無(wú)需通過(guò)Graal的maven插件buildtools)

    這篇文章主要介紹了Spring?Native打包本地鏡像,無(wú)需通過(guò)Graal的maven插件buildtools,本文探索一下,如果不通過(guò)這個(gè)插件來(lái)生成鏡像,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • jmeter中beanshell的用法小結(jié)

    jmeter中beanshell的用法小結(jié)

    本文主要介紹了jmeter中beanshell的用法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Java?BOI與NIO超詳細(xì)實(shí)例精講

    Java?BOI與NIO超詳細(xì)實(shí)例精講

    在Java的軟件設(shè)計(jì)開(kāi)發(fā)中,通信架構(gòu)是不可避免的,我們?cè)谶M(jìn)行不同系統(tǒng)或者不同進(jìn)程之間的數(shù)據(jù)交互,或者在高并發(fā)下的通信場(chǎng)景下都需要用到網(wǎng)絡(luò)通信相關(guān)的技術(shù),對(duì)于一些經(jīng)驗(yàn)豐富的程序員來(lái)說(shuō),Java早期的網(wǎng)絡(luò)通信架構(gòu)存在一些缺陷,這篇文章介紹Java?BOI與NIO
    2022-11-11
  • 關(guān)于Openfire集群源碼的分析

    關(guān)于Openfire集群源碼的分析

    這篇文章主要介紹了關(guān)于Openfire集群源碼的分析,內(nèi)容比較詳細(xì),具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • 詳解Spring Cloud Gateway基于服務(wù)發(fā)現(xiàn)的默認(rèn)路由規(guī)則

    詳解Spring Cloud Gateway基于服務(wù)發(fā)現(xiàn)的默認(rèn)路由規(guī)則

    這篇文章主要介紹了詳解Spring Cloud Gateway基于服務(wù)發(fā)現(xiàn)的默認(rèn)路由規(guī)則,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-05-05

最新評(píng)論

平罗县| 临海市| 奉新县| 新干县| 崇文区| 浦江县| 海兴县| 海盐县| 章丘市| 陵水| 吉林省| 增城市| 泸西县| 宁河县| 应城市| 景洪市| 禄丰县| 永登县| 监利县| 通河县| 赤壁市| 泰宁县| 余姚市| 承德市| 麦盖提县| 仙居县| 韩城市| 北碚区| 丰县| 图木舒克市| 英德市| 罗城| 定陶县| 延庆县| 海宁市| 泸水县| 霍邱县| 镇安县| 滨海县| 贺州市| 大英县|