天天看點

@RefreshScope動态重新整理配置實作原理

作者:專注着的部落格

在spring cloud alibaba nacos的配置中,我們為了能夠實作配置的動态重新整理,@RefreshScope則是其中的一個解決方案。這篇文章将會從被@RefreshScope标記的bean從類資訊搜集、建立、使用、重新整理、銷毀這樣一個過程入手,記錄Scope在spring中的工作原理。

1. @RefreshScope使用方式

@Getter
@Service
@RefreshScope
public class NacosValueService {

    @Value("${config.age}")
    private Integer age;
    private volatile boolean isStop = false;

    @PreDestroy
    public void destroy() {
        System.out.println("執行destroy方法");
    }
}           

通過在類上使用@RefreshScope注解,就能夠實作當配置config.age在發生變化之後,bean中的配置資訊也會發生變化。

2. @RefreshScope類資訊BeanDefinition建立

2.1 RefreshScope注解定義

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

    /**
     * @see Scope#proxyMode()
     * @return proxy mode
     */
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}           

在該注解定義中,包含了兩種資訊:

  • 該注解上除了定義注解的使用方式外,還加了一個@Scope注解,該注解定義了@Scope的名稱,常用的有:
    • singleton
    • prototype
  • 另外就是代理的方式,是以我們可以推斷,最終被@RefreshScope标記的bean, 最終是一個代理對象。

2.2 擷取BeanDefinition

在spring-boot環境中,當執行Application的時候,回去掃描目前classpath下的所有class檔案,并将對應的檔案解析為對應的BeanDefinition對象。

這裡隻考慮預設的掃描路徑,不考慮通過注解或者配置方式掃描其他路徑的情況

2.2.1 ClassPathBeanDefinitionScanner

該類主要為了掃描classpath下的類清單,并将對應的class檔案解析為BeanDefinition對象,我們主要檢視針對Scope注解對BeanDefinition影響的這部分。其他的不在讨論範圍。

doScan()

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 擷取路徑下的所有BeanDefinition清單
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 周遊所有的BeanDefinition
        for (BeanDefinition candidate : candidates) {
            // 解析@Scope注解中的内容, 以ScopeMetadata表示
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            // 更新BeanDefinition的scope值,預設為singleton
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成beanName, 這裡的beanName主要有兩種擷取方式:
            // 1. 判斷是否有注解,其中包含了Component, Service, Named, Indexed等注解,如果有,則判斷是否設定value值,如果設定,則以value的值為beanName
            // 2. 如果以上條件不滿足,則去class的名成,隻取類名,并将首字母轉換為小寫
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

            // 這裡判斷是否為AbstractBeanDefinition類型,如果是,則為BeanDefinition設定必要的參數, 包括初始化方法名稱等。
            // 不過在預設中,使用的都沒有名缺的指定
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 判斷是否為AnnotatedBeanDefinition對象,如果是,則執行通用的注解解析和使用:
            // 主要包含了以下幾類注解:
            // Lazy, Role, Primary, DependsOn, Description
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 檢查BeanDefinition是否已經被注冊,如果被注冊,則檢查兩者
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // 處理ScopeProxyMode屬性配置, 并生成新的DefintionHolder對象
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                // 加入beanDefinition清單
                beanDefinitions.add(definitionHolder);
                // 注冊目前FactoryBean的Definition到Regsitry中
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}           

在處理BeanDefinition資料中,最後通過AnnotationConfigUtils.applyScopedProxyMode()方法對BeanDefinition進行處理,在這個過程中,如果目前的BeanDefinition并非為NO時,這個時候在BeanDefinitionRegsitry中包含了兩個BeanDefinition, 是以需要特别注意。

  • 當ProxyMode=TARGET_CLASS時,這個時候被代理的BeanDefintion的名稱為scopedTarget.${origianlBeanName}
  • 另一個就是本身的BeanDefinition, 這時是一個FactoryBean的實作類,則對應的名稱為originalBeanName.

是以這兩者其實是一個替代關系,代理的FactoryBean指向了被代理的Bean.

2.2.2 AnnotationConfigUtils

該類主要是對注解配置類的解析,其中applyScopedMode()方法解決了不同代理的處理方式,具體源碼如下:

createScopedProxy()

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
            BeanDefinitionRegistry registry, boolean proxyTargetClass) {

        // 擷取初始beanName
        String originalBeanName = definition.getBeanName();
        // 擷取BeanDefinition
        BeanDefinition targetDefinition = definition.getBeanDefinition();
        // 擷取targetBeanName, 這裡格式為: `scopedTarget.${originalBeanName}`
        String targetBeanName = getTargetBeanName(originalBeanName);

        // Create a scoped proxy definition for the original bean name,
        // "hiding" the target bean in an internal target definition.
        // 這裡是為初始的BeanDefinition建立一個代理,隐藏目标的BeanDefinition
        RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
        proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
        proxyDefinition.setSource(definition.getSource());
        proxyDefinition.setRole(targetDefinition.getRole());

        // 設定TargetBeanName屬性,則指向了`scopedTarget.${originalBeanName}`BeanDefinition
        proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);

        if (proxyTargetClass) {
            // 如果ProxMode為TARGET_CLASS, 則設定屬性'org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass=true'
            targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
        }
        else {
            // 如果是其他的,則設定proxyTargetClass為false
            proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
        }

        // Copy autowire settings from original bean definition.
        // 從源BeanDefinition中拷貝屬性
        proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
        proxyDefinition.setPrimary(targetDefinition.isPrimary());
        if (targetDefinition instanceof AbstractBeanDefinition) {
            proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
        }

        // The target bean should be ignored in favor of the scoped proxy.
        targetDefinition.setAutowireCandidate(false);
        targetDefinition.setPrimary(false);

        // Register the target bean as separate bean in the factory.
        // 這裡需要注意,我們實際的beanName名稱是自定義或者指定,但是我們這裡處理完成了之後
        // 這裡就變成了`scopedTarget.${originalBeanName}`, 是以注冊的bean名稱發生了改變
        registry.registerBeanDefinition(targetBeanName, targetDefinition);

        // Return the scoped proxy definition as primary bean definition
        // (potentially an inner bean).
        // 傳回代理之後的BeanDefinition資訊
        return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }           

3. RefreshScope管理Bean

在講到對scope=refresh這類的BeanDefinition進行執行個體化的時候,需要講到的一個知識點就是對這類Bean的管理。我們知道, Spring預設管理兩類bean, 分别為scope=singleton和scope=prototype兩類,我們從bean的執行個體化過程就可以看出。而針對這兩類,其他的scope的bean則通過scope類型進行管理,是以,針對scope=refresh的管理,有專門的RefreshScope來實作。

我們檢視spring建立bean的過程中,scope的使用如下:

String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
    throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
    Object scopedInstance = scope.get(beanName, () -> { ...           

從代碼中,可以看出,主要包含以下步驟:

  • 擷取目前BeanDefinition原資訊的scope的值,并根據scope的值擷取對應的Scope對象
  • 如果Scope對象不存在,則抛出異常
  • 根據Scope對象的get方法建立bean對象執行個體。

3.1 類結構

3.2 注冊時機

從以上的類結構圖可以知道,因為GenericScope本身實作了BeanFactoryPostProcessor類,是以在BeanFactory準備完畢後,就會執行個體化該類并回調函數。是以我們檢視對應的實作方法。

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        beanFactory.registerScope(this.name, this);
        setSerializationId(beanFactory);
    }           

可以得知,在postProcessBeanFacotry()方法回調方法中,通過regsiterScope()方法将RefreshScope對象注冊到BeanFactory對象中,是以在建立Bean的時候能夠通過RefreshScope進行管理。

4. 建立Bean

在上面我們講到了,隻要對應的BeanDefintion的scope不是singleton或者prototype時,都會通過Scope對象來管理bean,具體代碼如下:

// 擷取scope
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
    throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
}
// 根據scope,擷取對應的Scope對象
Scope scope = this.scopes.get(scopeName);
// 如果沒有注冊Scope類型,将抛出錯誤
if (scope == null) {
    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
    // 從Scope中擷取bean對象,如果bean對象不存在,則通過函數建立bean
    Object scopedInstance = scope.get(beanName, () -> {
        beforePrototypeCreation(beanName);
        try {
            return createBean(beanName, mbd, args);
        }
        finally {
            afterPrototypeCreation(beanName);
        }
    });
    beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
    throw new ScopeNotActiveException(beanName, scopeName, ex);
}           

4.1 工廠類(FactoryBean)初始化

在上面我們談到,當我們的類需要做類型代理的時候,則會生成兩個BeanDefinition對象,是以originalBeanName則被FactoryBean所代替。在代理中,實際上使用的是ScopedProxyFactoryBean進行代替。

ScopedProxyFactoryBean

這個類實作是很簡單的,首先該類的類結構如下:

該類的實作是比較簡單的,主要包含了兩個元素:

  • targetBeanName: 被代理的bean名稱,之前我們知道,實在bean名稱前多加了一個targetBeanName.的字首
  • proxy:被代理bean對象的執行個體。
從初始化角度來說,該FactoryBean并沒有太多需要設定的内容。

4.2 代理對象proxy何時建立?

我們都知道,代理對象在最終擷取被代理對象的時候,都是通過getObject()方法來擷取,那麼我們通過檢視源碼,ScopedProxyFactoryBean的 proxy對象的建立,則是放在了setBeanFacotory()方法中。

public void setBeanFactory(BeanFactory beanFactory) {
    // 判斷beanFactory是否為ConfigurableBeanFacotry的執行個體
    if (!(beanFactory instanceof ConfigurableBeanFactory)) {
        throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
    }
    ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

    // scopedTargetSource預設為SimpleBeanTargetSource, 這裡綁定了BeanFactory
    this.scopedTargetSource.setBeanFactory(beanFactory);

    // 建立ProxyFactory執行個體
    ProxyFactory pf = new ProxyFactory();
    // 從目前的ProxyConfig拷貝屬性值到ProxyFactory中
    pf.copyFrom(this);
    // 設定被代理對象的資訊
    pf.setTargetSource(this.scopedTargetSource);

    Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
    // 擷取被代理對象的Class類型
    Class<?> beanType = beanFactory.getType(this.targetBeanName);
    // 如果beanType為空, 那麼對應的bean的class沒有被加載到BeanFactory中,則抛出異常
    if (beanType == null) {
        throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
                "': Target type could not be determined at the time of proxy creation.");
    }
    // 判斷是否為為代理目标對象,即判斷proxyTargetClass屬性值是否為false
    // 判斷代理對象的類型是否為接口
    // 判斷代理對象的類型定義是否為private
    // 當以上條件滿足一個時,那麼就擷取代理對象class的所有接口,并綁定到ProxyFactory對象中
    if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
        pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
    }

    // Add an introduction that implements only the methods on ScopedObject.
    // 建立DefaultScopedObject對象,
    ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
    // 向ProxyFacotry中新增DelegatingIntroductionInterceptor攔截器
    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.
    pf.addInterface(AopInfrastructureBean.class);

    // 建立代理對象并綁定到proxy屬性中
    this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}           

通過代碼可知,建立代理對象是通過ProxyFactory來完成的。

4.3 代理對象proxy建立過程

代理對象的建立主要通過ProxyFactory對象來完成,則檢視對應的源碼:

getProxy()

public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }           

該建立proxy主要通過createAopProxy()方法建立的類來實作。

createAopProxy()

protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }           

getAopProxyFactory()方法建立了工廠類對象,這裡使用了DefaultAopProxyFactory類來作為預設的工廠類對象。我們知道,spring有兩種代理方式:

  • JDK Proxy
    • 代理class是接口
    • 是Proxy生成的代理class
    • 是lambda表達式class
  • Cglib

這兩種方式有不同的選擇場景,則對應createAopProxy()代碼如下:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!NativeDetector.inNativeImage() &&
                (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
            // 擷取被代理對象的class
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            // 使用jdk的代理方式需要滿足一下條件:
            // 1. 是接口
            // 2. 是代理的class
            // 3. 是lambada表達式的class對象
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            // 其他情況使用Cglib
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }           

這裡我們主要關注下cglib的建立方式,因為對于被代理對象是接口的情況,其實我感覺在日常中使用的是比較少的,是以我們關注cglib建立方法。

cglib建立代理對象主要使用的asm生成位元組碼,這部分确實要對jvm有很高的認知的才會明白,cglib直接通過asm輸入位元組碼指令生成對應的class檔案。是以這裡就不做過多介紹。因為我也是雲裡霧裡。。。。

CglibAopProxy

getProxy()

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
    }

    try {
        // 擷取被代理對象的類型
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        // 被代理對象
        Class<?> proxySuperClass = rootClass;
        // 判斷對象的名稱中是否包含了$, 是以判斷是否本身為代理生成的class
        if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
            // 擷取超類
            proxySuperClass = rootClass.getSuperclass();
            // 擷取接口清單
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            // 将接口清單加入到advised對象中
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        // 驗證class
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            // 設定classloader對象
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        // 設定被代理的class對象
        enhancer.setSuperclass(proxySuperClass);
        // 設定代理對象class的接口清單
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        // 設定命名政策
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        // 設定生成政策
        enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

        // 擷取回調類清單
        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // 設定filter, 在代理的過程中,用于判斷那些方法不被代理
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.
        // 建立代理對象
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    catch (CodeGenerationException | IllegalArgumentException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                ": Common causes of this problem include using a final class or a non-visible class",
                ex);
    }
    catch (Throwable ex) {
        // TargetSource.getTarget() failed
        throw new AopConfigException("Unexpected AOP exception", ex);
    }
}           

createProxyClassAndInstance()

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    // 生成代理的class并加載class
    Class<?> proxyClass = enhancer.createClass();
    Object proxyInstance = null;

    if (objenesis.isWorthTrying()) {
        try {
            // 建立代理對象
            proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
        }
        catch (Throwable ex) {
            logger.debug("Unable to instantiate proxy using Objenesis, " +
                    "falling back to regular proxy construction", ex);
        }
    }

    // 如果代理對象建立失敗, 則通過構造器模式建立
    if (proxyInstance == null) {
        // Regular instantiation via default constructor...
        try {
            Constructor<?> ctor = (this.constructorArgs != null ?
                    proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
                    proxyClass.getDeclaredConstructor());
            ReflectionUtils.makeAccessible(ctor);
            proxyInstance = (this.constructorArgs != null ?
                    ctor.newInstance(this.constructorArgs) : ctor.newInstance());
        }
        catch (Throwable ex) {
            throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
                    "and regular proxy instantiation via default constructor fails as well", ex);
        }
    }
    // 為對象設定回調對象清單
    ((Factory) proxyInstance).setCallbacks(callbacks);
    return proxyInstance;           
這裡通過截圖可以看出,對于callback而言,他的内部不是存儲的清單,而是将清單拆分成單個成員變量。具體源碼可以參考cglib..

到此,對于FactoryBean的建立就到此結束。

4.4 Scope管理被代理對象

也許會有人會問,為什麼本身的bean初始化過程為什麼不講,其實很簡單,因為本身的bean就是一個普通對象的執行個體化過程,跟spring初始化一個bean的過程一樣,則沒有必要單獨再講一遍。

是以我們隻需要關注Scope在拿到了需要被Scope管的bean是如何存儲的即可。我們還是回到最開始的代碼,通過Scope擷取到Bean的代碼。

Object scopedInstance = scope.get(beanName, () -> {
    beforePrototypeCreation(beanName);
    try {
        return createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }
});           

GenericScope

在通過get方法擷取的時候,主要源碼如下:

public Object get(String name, ObjectFactory<?> objectFactory) {
    // 加入緩存
    BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory));
    // 加入鎖資訊
    this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
    try {
        return value.getBean();
    }
    catch (RuntimeException e) {
        this.errors.put(name, e);
        throw e;
    }
}           

這裡設計到兩個緩存資訊,主要緩存與bean有關的資訊。這裡主要關注BeanLifecycleWrapper類型的getBean()方法.

這裡有個點需要關注:

雖然cache類在每次都是put一個新的對象進入,但是内層的邏輯是會判斷目前緩存中是否包含了名稱為name的key,如果包含,是不會插入成功的,傳回的依然是之前已經緩存過的對象

其次就是在重新整理的時候,會清空緩存,這個時候才會将新的緩存對象放置到緩存中。

BeanLifecycleWrapper

public Object getBean() {
    // 判斷bean是否已經建立, 如果沒有建立,則直接建立
    if (this.bean == null) {
        // 鎖定名稱,讓統一時間隻有一個線程建立bean
        synchronized (this.name) {
            // 如果bean為空,則調用ObjectFactory建立bean
            if (this.bean == null) {
                this.bean = this.objectFactory.getObject();
            }
        }
    }
    return this.bean;
}           

是以,在BeanLifecycleWrapper類型中,實際上是會緩存bean對象,避免重複建立。

5. 清空bean

當bean中的配置在發生變更之後,spring主要通過事件機制判斷是否需要重新整理bean, 當接受需要重新整理事件之後,則會将scope中所有的緩存清空。

RefreshEventListener

@Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationReadyEvent) {
            handle((ApplicationReadyEvent) event);
        }
        // 接受到重新整理事件
        else if (event instanceof RefreshEvent) {
            handle((RefreshEvent) event);
        }
    }


    public void handle(RefreshEvent event) {
        if (this.ready.get()) { // don't handle events before app is ready
            log.debug("Event received " + event.getEventDesc());
            // 執行refresh操作
            Set<String> keys = this.refresh.refresh();
            log.info("Refresh keys changed: " + keys);
        }
    }           

從代碼中可以知道,在目前類中主要處理兩類事件,一類是ApplicationReadyEvent,另一類就是RefreshEvent. 當接受到重新整理的時候,會執行ContextRefresher類的重新整理方法。

ContextRefresher

public synchronized Set<String> refresh() {
        Set<String> keys = refreshEnvironment();
        this.scope.refreshAll();
        return keys;
    }           

在重新整理的過程中,主要包含了兩個步驟:

  • 重新整理目前的環境配置,這個在之前的文章中已經詳細介紹過
  • 重新整理Scope中的緩存。

我們可以看下重新整理緩存的實作邏輯:

RefreshScope

public void refreshAll() {
        super.destroy();
        this.context.publishEvent(new RefreshScopeRefreshedEvent());
    }           

這裡主要包含了兩個步驟:

  • 第一個就是執行destroty方法,在執行destroy()方法的時候,會将儲存在scope中的bean進行周遊,然後執行銷毀bean的邏輯
  • 再者就是發送一個RefreshScopeRefreshedEvent事件,用于通知重新整理scope成功

以上就是@RefreshScope的原理介紹,希望對大家有所幫助。

繼續閱讀