中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

@RereshScope刷新的原理是什么

發布時間:2022-12-05 09:30:19 來源:億速云 閱讀:128 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“@RereshScope刷新的原理是什么”,內容詳細,步驟清晰,細節處理妥當,希望這篇“@RereshScope刷新的原理是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

    在配合配置中心修改配置讓應用自動刷新配置時,我們要在需要感知配置變化的bean上面加上@RereshScope。如果我們不加上這注解,那么有可能無法完成配置自動刷新。

    一、入口

    可以看到@RereshScope@Scope("refresh")(bean的作用域)的派生注解并指定了作用域為refresh并在默認情況下proxyMode= ScopedProxyMode.TARGET_CLASS使用CGLIB生成代理對象

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

    ScopedProxyMode

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

    • DEFAULT:默認no

    • NO:不使用代理

    • INTERFACES:使用JDK動態代理

    • TARGET_CLASS:使用CGLIB動態代理

    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;
    }

    二、配置類解析

    在上文刷新時會執行BeanFacotryPostProcessorbeanDefinition修改和增加,其中配置類解析、類掃描的工作就是在其中執行,而對于一個ScopedProxyMode.NO它會解析成一個ScopedProxyFactoryBean

    //ConfigurationClassBeanDefinitionReader配置類解析代碼片段
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    
    //如果需要生產代理則創建一個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-生成代理對象

    FactoryBean在注入時返回的是getObject()所返回的對象,在這里就是返回的就是proxyScopedProxyFactoryBean實現了BeanFactoryAware那么在這個bean初始化中會調用setBeanFactory()方法,而在這個方法中,為它創建一個CGLIB代理對象作為getObject()的返回值,并使用ScopedObject來代替被代理對象。而在ScopedObject默認實現中每次都是從BeanFactory中獲取(重點)。

    @Override
    public Object getObject() {
    	if (this.proxy == null) {
    		throw new FactoryBeanNotInitializedException();
    	}
        //返回代理對象
    	return this.proxy;
    }
    @Override
    public 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 {
        //...省略字段信息和構造器
    	@Override
    	public Object getTargetObject() {
            //從容器中獲取
    		return this.beanFactory.getBean(this.targetBeanName);
    	}
    	@Override
    	public void removeFromScope() {
    		this.beanFactory.destroyScopedBean(this.targetBeanName);
    	}
    }

    三、作用域原理

    BeanFactory獲取bean時(doGetBean),如果**不是單例或者原型bean**將交給對應的Socpebean,而創建bean方式和單例bean是一樣的。其他作用域像requestsession等等都是屬于這一塊的擴展:SPI+策略模式

    //AbstractBeanFactory doGetBean()代碼片段
    String scopeName = mbd.getScope();
    //獲取對應的scope
    final 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刷新的原理是什么”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    紫云| 基隆市| 云安县| 邢台市| 调兵山市| 天长市| 临西县| 土默特右旗| 枞阳县| 综艺| 芦山县| 江西省| 融水| 台州市| 凭祥市| 巴彦淖尔市| 陵水| 阿巴嘎旗| 屯门区| 临海市| 沛县| 普格县| 甘洛县| 封开县| 江达县| 福贡县| 改则县| 安宁市| 邛崃市| 广水市| 临朐县| 星座| 南郑县| 紫阳县| 南川市| 西林县| 南阳市| 南通市| 眉山市| 阳高县| 梁河县|