SpringCloud @RefreshScope实现原理
Posted OkidoGreen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud @RefreshScope实现原理相关的知识,希望对你有一定的参考价值。
环境: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。
以上是关于SpringCloud @RefreshScope实现原理的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud+@RefreshScope+@Value配置中心动态加载(刷新)配置文件
SpringCloud+@RefreshScope+@Value配置中心动态加载(刷新)配置文件