您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“@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
表示作用域的代理模式,共有以下四個值:
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; }
在上文刷新時會執行BeanFacotryPostProcessor
對beanDefinition
修改和增加,其中配置類解析、類掃描的工作就是在其中執行,而對于一個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);
FactoryBean
在注入時返回的是getObject()
所返回的對象,在這里就是返回的就是proxy
。ScopedProxyFactoryBean
實現了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()); }
作用域對象的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
**將交給對應的Socpe
去bean
,而創建bean
方式和單例bean是一樣的。其他作用域像request
、session
等等都是屬于這一塊的擴展: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刷新的原理是什么”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。