<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在配合設定中心修改設定讓應用自動重新整理設定時,我們要在需要感知設定變化的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
:預設noNO
:不使用代理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
會為這個標記的bean生成ScopedProxyFactoryBean
,ScopedProxyFactoryBean
生成一個代理物件使每次方法呼叫的目標物件都從容器中獲取,而獲取refresh
作用域的Bean是RefreshScope
快取中獲取。當設定中心在觸發重新整理時RefreshScope
會刪除Socpe
快取的Bean,然後下次獲取時就會用新的Environment
建立設定修改後的Bean,這樣就達到了設定的自動更新。
為什麼需要生成代理物件?
因為Bean裝配是一次性的,假設沒有代理的情況下,在另一個bean注入這個refreshBean
之後就無法改變了,就算refreshBean
銷燬(在快取中置為null)並後面重新生成了,但是之前參照還是老的bean
,這也是為什麼沒有加@RefreshScope
註解而導致設定自動重新整理失效了。
到此這篇關於@RereshScope重新整理的原理詳解的文章就介紹到這了,更多相關@RereshScope重新整理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45