# 一、如何实现动态配置
在Spring体系下,如果实现了ConfigurationProperties则会自动刷新。而如果只使用@Value
的方法,要加上 @RefreshScope
才能实现。 本篇文章我们来分别研究下他们的原理。然后在来看看其他的方案是如何做的吧。
# 二、实现原理
# 2.1 @ConfigurationProperties
所有被@ConfigurationProperties
修饰的类都会被ConfigurationPropertiesBeans处理
- 实现
BeanPostProcessor
处理器,初始化时候判断是否被@ConfigurationProperties
修饰,如果是就保存到ConfigurationPropertiesBeans#beans属性中
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {// 1. 如果已经被RefreshScope修饰了,也会自动更新就不用在处理了。 if (isRefreshScoped(beanName)) {return bean;}ConfigurationProperties annotation = AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class);if (annotation != null) {this.beans.put(beanName, bean);}else if (this.metaData != null) {annotation = this.metaData.findFactoryAnnotation(beanName,ConfigurationProperties.class);if (annotation != null) {this.beans.put(beanName, bean);}}return bean;}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
- ConfigurationPropertiesRebinder 实现
EnvironmentChangeEvent
变更事件, 当收到EnvironmentChangeEvent事件 会重新触发绑定事件。需要绑定的bean就从ConfigurationPropertiesBeans#beans属性中获取。
具体的实现类 ConfigurationPropertiesRebinder
- 先调用销毁方法
- 然后重新初始化
// 接受事件public void onApplicationEvent(EnvironmentChangeEvent event) {if (this.applicationContext.equals(event.getSource())// Backwards compatible|| event.getKeys().equals(event.getSource())) {rebind();}}// 重新绑定public boolean rebind(String name) {if (!this.beans.getBeanNames().contains(name)) {return false;}if (this.applicationContext != null) {try {Object bean = this.applicationContext.getBean(name);if (AopUtils.isAopProxy(bean)) {bean = ProxyUtils.getTargetObject(bean);}if (bean != null) {this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);return true;}}catch (RuntimeException e) {this.errors.put(name, e);throw e;}catch (Exception e) {this.errors.put(name, e);throw new IllegalStateException("Cannot rebind to " + name, e);}}return false;}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
# 2.2 @RefreshScope
@RefreshScope
的原理相对流程较长,首先他需要你将类用 @RefreshScope
来修饰。
- 首先明确那些是被修饰的
AnnotatedBeanDefinitionReader#registerBean
。
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {return;}abd.setInstanceSupplier(instanceSupplier);ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());...BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);// 创建bean描述信息 beanClass = ScopedProxyFactoryBean// ScopedProxyCreator#createScopedProxy->ScopedProxyUtils#createScopedProxydefinitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
- 被Scope修饰的beanClass都是ScopedProxyFactoryBean
- GenericScope 实现BeanFactoryPostProcessor 会提前将RefreshScope注册到BeanFactory中
- beanFactory.registerScope(this.name, this)
- 当执行完上面 AbstractBeanFactory#scopes属性中就有值了。对于RefreshScope name = refresh
public class GenericScope implements Scope, BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor, DisposableBean {}
public class RefreshScope extends GenericScope implements ApplicationContextAware,ApplicationListener<ContextRefreshedEvent>, Ordered {
}
1 2 3 4 5 6 7
- 当getBean时候,对于域对象会有特殊的处理逻辑,会调用
Scope#get(String name, ObjectFactory<?> objectFactory)
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {...// 创建单例逻辑if (mbd.isSingleton()) {...bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 创建原型逻辑else if (mbd.isPrototype()) {...bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {// 创建域对象// refreshString scopeName = mbd.getScope();// RefreshScopefinal 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, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}}}}return (T) bean;}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
public interface Scope {Object get(String name, ObjectFactory<?> objectFactory);
}
public class GenericScope implements Scope, BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor, DisposableBean {}
public class RefreshScope extends GenericScope implements ApplicationContextAware,ApplicationListener<ContextRefreshedEvent>, Ordered {}
1 2 3 4 5 6 7
- RefreshEventListener 接受事件,触发刷新操作
public class RefreshEventListener implements SmartApplicationListener {private ContextRefresher refresh;@Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationReadyEvent) {handle((ApplicationReadyEvent) event);}else if (event instanceof RefreshEvent) {handle((RefreshEvent) event);}}public void handle(ApplicationReadyEvent event) {this.ready.compareAndSet(false, true);}public void handle(RefreshEvent event) {if (this.ready.get()) { // don't handle events before app is readylog.debug("Event received " + event.getEventDesc());Set<String> keys = this.refresh.refresh();log.info("Refresh keys changed: " + keys);}}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
- ContextRefresher#refresh
- refreshEnvironment刷新环境
- 调用RefreshScope#refreshAll
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();this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));return keys;}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
- RefreshScope#refreshAll 会将容器中的bean给销毁。 而ScopedProxyFactoryBean中getObject是一个代理对象。带代理类每次都从容器中获取。而容器前面已经将被RefreshScope修饰的类给销毁了 测试拿到的对象就是重新从容器中生成的。
public class ScopedProxyFactoryBean extends ProxyConfigimplements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {private Object proxy; private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();@Overridepublic void setBeanFactory(BeanFactory beanFactory) {...ProxyFactory pf = new ProxyFactory();pf.copyFrom(this);pf.setTargetSource(this.scopedTargetSource);this.proxy = pf.getProxy(cbf.getBeanClassLoader());}
} public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {@Overridepublic Object getTarget() throws Exception {return getBeanFactory().getBean(getTargetBeanName());}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# 三、其他方案
因为我们项目中用的是阿波罗,那我们只看阿波罗是如何来做的吧。 在阿波罗只用使用@Value就行了
# 3.1 先扫描@Value注解
将被@Value修饰的Bean和配置key先生成一个SpringValue
对象然后注册到SpringValueRegistry
中
public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {protected void processField(Object bean, String beanName, Field field) {// register @Value on fieldValue value = field.getAnnotation(Value.class);if (value == null) {return;}Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());if (keys.isEmpty()) {return;}for (String key : keys) {SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);springValueRegistry.register(beanFactory, key, springValue);logger.debug("Monitoring {}", springValue);}}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# 3.2 找到需要更新的Bean
接受到配置变更事件后,遍历本地变更的配置key,然后将本次key关联需要变更的Bean,从springValueRegistry
中找到。
public class AutoUpdateConfigChangeListener implements ConfigChangeListener{@Overridepublic void onChange(ConfigChangeEvent changeEvent) {Set<String> keys = changeEvent.changedKeys();if (CollectionUtils.isEmpty(keys)) {return;}for (String key : keys) {// 1. check whether the changed key is relevantCollection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);if (targetValues == null || targetValues.isEmpty()) {continue;}// 2. update the valuefor (SpringValue val : targetValues) {updateSpringValue(val);}}}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# 3.3 通过反射的方法注入
public class SpringValue {public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {if (isField()) {injectField(newVal);} else {injectMethod(newVal);}}private void injectField(Object newVal) throws IllegalAccessException {Object bean = beanRef.get();if (bean == null) {return;}boolean accessible = field.isAccessible();field.setAccessible(true);field.set(bean, newVal);field.setAccessible(accessible);}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
非常简单,高效。相比使用@RefreshScope是不是清爽多了呢?