環境:spring cloud context2.2.8.RELEASE + spring boot 2.3.9.RELEASE
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("refresh")注解指明了作用域名為refresh。
2 注冊RefreshScope
public class RefreshAutoConfiguration { /** * Name of the refresh scope name. */ public static final String REFRESH_SCOPE_NAME = "refresh"; // 注冊RefreshScope @Bean @ConditionalOnMissingBean(RefreshScope.class) public static RefreshScope refreshScope() { return new RefreshScope(); } // 主要功能是刷新配置文件,發布事件(事件監聽程序接收到事件后會重寫配置相關的配置類) @Bean @ConditionalOnMissingBean public ContextRefresher contextRefresher(ConfigurableApplicationContext context, RefreshScope scope) { return new ContextRefresher(context, scope); } // 監聽上下文刷新事件 @Bean public RefreshEventListener refreshEventListener(ContextRefresher contextRefresher) { return new RefreshEventListener(contextRefresher); } }
RefreshScope類
該類的核心方法都在父類中實現
public class RefreshScope extends GenericScope implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, Ordered { public boolean refresh(String name) { if (!name.startsWith(SCOPED_TARGET_PREFIX)) { name = SCOPED_TARGET_PREFIX + name; } if (super.destroy(name)) { this.context.publishEvent(new RefreshScopeRefreshedEvent(name)); return true; } return false; } @ManagedOperation(description = "Dispose of the current instance of all beans " + "in this scope and force a refresh on next method execution.") public void refreshAll() { super.destroy(); this.context.publishEvent(new RefreshScopeRefreshedEvent()); } }
該類繼承自GenericScope,而GenericScope繼承BeanFactoryPostProcessor。
public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; beanFactory.registerScope(this.name, this); setSerializationId(beanFactory); } }
在這里會注冊RefreshScope。注冊的Scope將會在AbstractBeanFactory#doGetBean方法中調用,該方法中會先拿到當前BeanDefinition中定義的Scope,通過scopeName從Map集合中拿到Scope類,最后調用Scope的get方法獲取實例對象;詳細參見15例,查看get方法調用時機。
添加了@RefreshScope注解的Bean對象會執行RefreshScope#get方法
public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean { private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(new StandardScopeCache()); // 將要獲取的Bean緩存上 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; } } }
3 Refresh端點觸發時機
觸發機制基於事件驅動,發布EnvironmentChangeEvent事件。
當調用/actuator/refresh端點時,執行如下refresh:
@Endpoint(id = "refresh") public class RefreshEndpoint { private ContextRefresher contextRefresher; public RefreshEndpoint(ContextRefresher contextRefresher) { this.contextRefresher = contextRefresher; } @WriteOperation public Collection<String> refresh() { Set<String> keys = this.contextRefresher.refresh(); return keys; } }
調用ContextRefresher#refresh方法
public class ContextRefresher { public synchronized Set<String> refresh() { Set<String> keys = refreshEnvironment(); this.scope.refreshAll(); return keys; } public synchronized Set<String> refreshEnvironment() { // 配置信息修改之前的值 Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources()); // 重新加載讀取配置信息 addConfigFilesToEnvironment(); // 獲取所有改變的配置 Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); // 發布事件(處理事件中所有配置類@ConfigurationProperties) this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys)); return keys; } }
在refreshEnvironment方法中會發布一個EnvironmentChangeEvent事件;查看該事件的監聽器
public class ConfigurationPropertiesRebinder implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> { private ConfigurationPropertiesBeans beans; private ApplicationContext applicationContext; private Map<String, Exception> errors = new ConcurrentHashMap<>(); public ConfigurationPropertiesRebinder(ConfigurationPropertiesBeans beans) { this.beans = beans; } @ManagedOperation public void rebind() { this.errors.clear(); // 遍歷所有的配置類(帶有@ConfigurationProperties注解的類) for (String name : this.beans.getBeanNames()) { // 對每一個bean進行重新綁定 rebind(name); } } @ManagedOperation public boolean rebind(String name) { if (!this.beans.getBeanNames().contains(name)) { return false; } if (this.applicationContext != null) { Object bean = this.applicationContext.getBean(name); if (AopUtils.isAopProxy(bean)) { bean = ProxyUtils.getTargetObject(bean); } if (bean != null) { if (getNeverRefreshable().contains(bean.getClass().getName())) { return false; // ignore } // 銷毀當前的bean this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean); // 初始化Bean this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name); return true; } } return false; } @ManagedAttribute public Set<String> getBeanNames() { return new HashSet<>(this.beans.getBeanNames()); } @Override public void onApplicationEvent(EnvironmentChangeEvent event) { if (this.applicationContext.equals(event.getSource()) || event.getKeys().equals(event.getSource())) { rebind(); } } }
當收到EnvironmentChangeEvent事件后執行rebind方法。在該類中的beans是通過構造函數傳過來的,接下來先查看這個對象是如何被構造的
public class ConfigurationPropertiesRebinderAutoConfiguration implements ApplicationContextAware, SmartInitializingSingleton { @Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) public static ConfigurationPropertiesBeans configurationPropertiesBeans() { return new ConfigurationPropertiesBeans(); } @Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) public ConfigurationPropertiesRebinder configurationPropertiesRebinder(ConfigurationPropertiesBeans beans) { ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder(beans); return rebinder; } }
在這個自動配置類中創建了
ConfigurationPropertiesRebinder並且將ConfigurationPropertiesBeans 注入。ConfigurationPropertiesBeans是個BeanPostProcessor處理器
@Component public class ConfigurationPropertiesBeans implements BeanPostProcessor, ApplicationContextAware { // 添加有@ConfigurationProperties注解的bean都將保存在該集合中 private Map<String, ConfigurationPropertiesBean> beans = new HashMap<>(); private ApplicationContext applicationContext; private ConfigurableListableBeanFactory beanFactory; private String refreshScope; private boolean refreshScopeInitialized; private ConfigurationPropertiesBeans parent; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (isRefreshScoped(beanName)) { return bean; } ConfigurationPropertiesBean propertiesBean = ConfigurationPropertiesBean .get(this.applicationContext, bean, beanName); if (propertiesBean != null) { this.beans.put(beanName, propertiesBean); } return bean; } private boolean isRefreshScoped(String beanName) { if (this.refreshScope == null && !this.refreshScopeInitialized) { this.refreshScopeInitialized = true; for (String scope : this.beanFactory.getRegisteredScopeNames()) { if (this.beanFactory.getRegisteredScope( scope) instanceof org.springframework.cloud.context.scope.refresh.RefreshScope) { this.refreshScope = scope; break; } } } if (beanName == null || this.refreshScope == null) { return false; } return this.beanFactory.containsBeanDefinition(beanName) && this.refreshScope.equals(this.beanFactory.getBeanDefinition(beanName).getScope()); } public Set<String> getBeanNames() { return new HashSet<String>(this.beans.keySet()); } }
該BeanPostProcessor的
postProcessBeforeInitialization方法執行在初始化bean的時候執行在這里就會判斷當前的Bean是否是RefreshScope Bean。
在isRefreshScoped方法中遍歷注冊的所有Scope並且判斷是否是有RefreshScope,先從注冊的所有Scope中查找RefreshScope,如果沒有返回false,如果是則返回true。如果isRefreshScoped方法返回的false就判斷當前Bean是否有@ConfigurationProperties注解如果有會被包裝成
ConfigurationPropertiesBean存入當前的beans集合中(當有refresh發生時會重新綁定這些bean)。接下來繼續進入到上面的ConfigurationPropertiesRebinder#rebind方法中。
rebind方法中會對所有帶有@ConfigurationProperties注解的類進行刷新,rebind方法中會對bean進行銷毀和初始化。
this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean); this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);
4 RefreshScope刷新處理
當刷新refresh被調用后會執行RefreshScope#refreshAll方法
public class ContextRefresher { public synchronized Set<String> refresh() { Set<String> keys = refreshEnvironment(); this.scope.refreshAll(); return keys; } }
RefreshScope#refreshAll方法
public void refreshAll() { // 調用RefreshScope父類的destroy方法 super.destroy(); // 發布RefreshScopeRefreshedEvent事件,我們可以寫個監聽程序監聽該事件 this.context.publishEvent(new RefreshScopeRefreshedEvent()); }
父類GenriceScope#destroy方法
public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
@Override
public void destroy() {
List<Throwable> errors = new ArrayList<Throwable>();
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
for (BeanLifecycleWrapper wrapper : wrappers) {
try {
Lock lock = this.locks.get(wrapper.getName()).writeLock();
lock.lock();
try {
wrapper.destroy();
} finally {
lock.unlock();
}
} catch (RuntimeException e) {
errors.add(e);
}
}
if (!errors.isEmpty()) {
throw wrapIfNecessary(errors.get(0));
}
this.errors.clear();
}
}
wrapper#destroy方法
private static class BeanLifecycleWrapper { private final String name; private final ObjectFactory<?> objectFactory; private Object bean; private Runnable callback; BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory) { this.name = name; this.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) { return; } synchronized (this.name) { Runnable callback = this.callback; if (callback != null) { callback.run(); } this.callback = null; this.bean = null; } } }
當前清空了緩存對象后,下次再進入注入的時候會再次調用ObjectFacotry#getObject方法創建新的對象。
總結:當觸發了refresh后,所有的帶有@ConfigurationProperties注解的Bean都會自動的刷新並不需要@RefreshScope注解。而有@RefreshScope注解的一般在應用在非配置類上,有成員屬性使用@Value注解的,如下:
@RestController @RequestMapping("/refreshBeanProp") @RefreshScope public class RefreshScopeBeanPropController { @Value("${custom}") private String custom ; @GetMapping("/get") public String get() { return custom ; } }
在此種情況下,調用/actuator/refresh 可使custom動態刷新,在ContextRefresher#refresh中將緩存的Bean清空了后重新生成Bean。
完畢!!!
給個關注+轉發唄謝謝
眾:Springboot實戰案例錦集