在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的原理介紹,希望對大家有所幫助。