配置中心动态刷新原理

前言

很容易想到想要实现动态刷新,至少需要做到以下两点

  • 刷新属性配置,即Environment实例中的PropertySource
  • 刷新对应的bean,以及依赖当前刷新bean的其它bean。

第一点看着还好,第二点难道要重新实例化要刷新的bean a,然后再找到相关其他的bean,把这个重新实例化的bean a注入进去吗?

刷新属性配置

SpringCloud刷新配置和Bean的入口为ContextRefresher,然后自动配置类为RefreshAutoConfiguration

ContextRefresher一个是抽象类,目前有两个实例

  • LegacyContextRefresher,以前老的方式,如果应用开启了bootstrap上下文,则使用这个
  • ConfigDataContextRefresher,在Spring Boot 2.4.0以后版本,可以使用spring.config.import导入外部配置,可以不再需要bootstrap上下文

ContextRefresher.java

/**
 * 入口方法,刷新环境以及bean
 */
public synchronized Set<String> refresh() {
    // 刷新环境
    Set<String> keys = refreshEnvironment();
    // 刷新bean
    this.scope.refreshAll();
    return keys;
}

public synchronized Set<String> refreshEnvironment() {
    // 之前的属性配置
    Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources());
    // 更新属性配置到环境中, 子类实现
    updateEnvironment();
    // 变化的key
    Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();
    this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
    return keys;
}

LegacyContextRefresher.java

@Override
protected void updateEnvironment() {
    addConfigFilesToEnvironment();
}

ConfigurableApplicationContext addConfigFilesToEnvironment() {
    ConfigurableApplicationContext capture = null;
    try {
        StandardEnvironment environment = copyEnvironment(getContext().getEnvironment());

        Map<String, Object> map = new HashMap<>();
        map.put("spring.jmx.enabled", false);
        map.put("spring.main.sources", "");
        // gh-678 without this apps with this property set to REACTIVE or SERVLET fail
        map.put("spring.main.web-application-type", "NONE");
        map.put(BOOTSTRAP_ENABLED_PROPERTY, Boolean.TRUE.toString());
        environment.getPropertySources().addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));

        SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class).bannerMode(Banner.Mode.OFF)
                .web(WebApplicationType.NONE).environment(environment);
        // Just the listeners that affect the environment (e.g. excluding logging
        // listener because it has side effects)
        // 只设置2个开启bootstrap上下文的和加载配置文件的Listener
        builder.application().setListeners(
                Arrays.asList(new BootstrapApplicationListener(), new BootstrapConfigFileApplicationListener()));
        capture = builder.run();
        if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
            environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
        }
        MutablePropertySources target = getContext().getEnvironment().getPropertySources();
        String targetName = null;
        for (PropertySource<?> source : environment.getPropertySources()) {
            String name = source.getName();
            if (target.contains(name)) {
                targetName = name;
            }
            if (!this.standardSources.contains(name)) {
                if (target.contains(name)) {
                    target.replace(name, source);
                }
                else {
                    if (targetName != null) {
                        target.addAfter(targetName, source);
                        // update targetName to preserve ordering
                        targetName = name;
                    }
                    else {
                        // targetName was null so we are at the start of the list
                        target.addFirst(source);
                        targetName = name;
                    }
                }
            }
        }
    }
    finally {
        ConfigurableApplicationContext closeable = capture;
        while (closeable != null) {
            try {
                closeable.close();
            }
            catch (Exception e) {
                // Ignore;
            }
            if (closeable.getParent() instanceof ConfigurableApplicationContext) {
                closeable = (ConfigurableApplicationContext) closeable.getParent();
            }
            else {
                break;
            }
        }
    }
    return capture;
}

因为应用有开启bootstrap上下文,加载配置中心的配置依赖bootstrap上下文,因此使用SpringApplication重新走了一遍Spring Boot的启动流程,重新初始化了Environment实例,然后把PropertySource应用到原来的Environment实例中。

ConfigDataContextRefresher.java

@Override
protected void updateEnvironment() {
    if (logger.isTraceEnabled()) {
        logger.trace("Re-processing environment to add config data");
    }
    StandardEnvironment environment = copyEnvironment(getContext().getEnvironment());
    ConfigurableBootstrapContext bootstrapContext = getContext().getBeanProvider(ConfigurableBootstrapContext.class)
            .getIfAvailable(DefaultBootstrapContext::new);

    // run thru all EnvironmentPostProcessor instances. This lets things like vcap and
    // decrypt happen after refresh. The hard coded call to
    // ConfigDataEnvironmentPostProcessor.applyTo() is now automated as well.
    DeferredLogFactory logFactory = new PassthruDeferredLogFactory();
    // 加载EnvironmentPostProcessor
    List<String> classNames = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class,
            getClass().getClassLoader());
    Instantiator<EnvironmentPostProcessor> instantiator = new Instantiator<>(EnvironmentPostProcessor.class,
            (parameters) -> {
                parameters.add(DeferredLogFactory.class, logFactory);
                parameters.add(Log.class, logFactory::getLog);
                parameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
                parameters.add(BootstrapContext.class, bootstrapContext);
                parameters.add(BootstrapRegistry.class, bootstrapContext);
            });
    List<EnvironmentPostProcessor> postProcessors = instantiator.instantiate(classNames);
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessEnvironment(environment, application);
    }

    if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
        environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
    }
    MutablePropertySources target = getContext().getEnvironment().getPropertySources();
    String targetName = null;
    for (PropertySource<?> source : environment.getPropertySources()) {
        String name = source.getName();
        if (target.contains(name)) {
            targetName = name;
        }
        if (!this.standardSources.contains(name)) {
            if (target.contains(name)) {
                target.replace(name, source);
            }
            else {
                if (targetName != null) {
                    target.addAfter(targetName, source);
                    // update targetName to preserve ordering
                    targetName = name;
                }
                else {
                    // targetName was null so we are at the start of the list
                    target.addFirst(source);
                    targetName = name;
                }
            }
        }
    }
}

与老的相比,因为不再需要使用bootstrap上下文,因此只需要走一遍EnvironmentPostProcessor逻辑即可,便能加载所有的配置信息了。

总结:可以看到spring cloud是将PropertySource完整替换掉,而不是只是其中变动的配置项

刷新bean

Spring Cloud只会对被@RefreshScope@ConfigurationProperties标注的bean进行刷新。

主要原理:对于被@RefreshScope标注的bean,其实在容器中会注册两个BeanDefinition,一个是代理对象,一个是目标对象,代理对象在调用方法时,会先调用容器的getBean方法拿到目标对象,然后用这个目标对象调用方法。因此只需要在刷新后让容器中的目标对象失效,然后重新创建便能达到动态刷新效果,而且还不用管那些注入过这个bean的其他bean,因为注入的是代理对象,而代理对象引用并没有发生任何变化。

区别:

  • 代理:单例,可以被注入,beanName为定义的beanName,父类为目标类
  • 目标类,scope=refresh,不可以被注入,beanName会带一个scopedTarget.前缀

先通过以下例子来看下刷新前后bean实例的变化。

@Component
@RefreshScope
public class ScopeBean {
}

@Getter
@Component
public class AutowireScopeBean {

    @Autowired
    private ScopeBean scopeBean;

}

测试类

@SpringBootTest
public class ApplicationTests {

    @Autowired
    private ConfigurableApplicationContext ac;

    @Autowired
    private ContextRefresher contextRefresher;

    @Autowired
    private AutowireScopeBean autowireScopeBean;

    @Test
    public void contextLoad() {

        String[] beanNames = ac.getBeanNamesForType(ScopeBean.class);
        // 目标对象beanName
        Assertions.assertEquals("scopedTarget.scopeBean", beanNames[0]);
        // 代理对象beanName
        Assertions.assertEquals("scopeBean", beanNames[1]);

        // 代理bean
        ScopeBean beanBefore = ac.getBean(ScopeBean.class);
        System.out.println(beanBefore.getClass());
        System.out.println(beanBefore.getClass().getSuperclass());
        // 目标bean
        ScopeBean targetBeanBefore1 = (ScopeBean) ac.getBean("scopedTarget.scopeBean");
        ScopeBean targetBeanBefore2 = (ScopeBean) ac.getBean("scopedTarget.scopeBean");
        // 未刷新之前, 获取的目标对象为同一个
        Assertions.assertSame(targetBeanBefore1, targetBeanBefore2);
        // 刷新
        contextRefresher.refresh();
        ScopeBean beanAfter = ac.getBean(ScopeBean.class);
        // 刷新后, 代理对象引用不会发生变化, 为同一个对象
        Assertions.assertSame(beanBefore, beanAfter);
        Assertions.assertSame(beanBefore, autowireScopeBean.getScopeBean());
        ScopeBean targetBeanAfter = (ScopeBean) ac.getBean("scopedTarget.scopeBean");
        // 刷新后,目标对象和刷新前为不同的对象
        Assertions.assertNotSame(targetBeanBefore1, targetBeanAfter);

    }
}

代码分析

动态刷新借助了Spring中的scope概念来实现的,最熟悉的应该便是singleton、prototype吧,默认情况下注册的bean都是singleton的,这些单例bean都被放在DefaultListableBeanFactory中的singletonObjects(实际是DefaultSingletonBeanRegistry,继承的)属性中,也就是一个Map。

前置知识

容器getBean方法部分片段

// Create bean instance.
// 单例对象
if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    });
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型对象
else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        afterPrototypeCreation(beanName);
    }
    beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

else {
    String scopeName = mbd.getScope();
    if (!StringUtils.hasLength(scopeName)) {
        throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
    }
    Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {
        // 通过对应Scope的get方法获取
        Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
                return createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
        });
        beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    catch (IllegalStateException ex) {
        throw new ScopeNotActiveException(beanName, scopeName, ex);
    }
}

可以看到,如果scope属性不是singleton、prototype,是从Scope对象的get方法获取的。

@Scope.java

public @interface Scope {

    /**
     * scope名字
     * 目前有prototype、singleton
     * servlet web环境增加了request, session
     * spring cloud增加了refresh, local
     * 其它的值会报错,当然也可以自己扩展,增加对应的名字实现就能使用了
     * 默认值为空字符串,相当于singleton
     */
    String value() default "";

    /**
     * 同value
     */
    @AliasFor("value")
    String scopeName() default "";

    /**
     * 是否需要创建代理
     * DEFAULT会跟随全局,基本相当于NO
     */
    ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

}
@RefreshScope.java
/**
 * 定义scopeName=refresh
 * 并且使用cglib方式创建代理
 */
@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;

}
修改BeanDefinition

上文说到会注册两个bean到容器中,这个会根据proxyMode的属性来决定是否需要注册代理beanDefinition定义。

AnnotationConfigUtils.java(这只是一个其中入口,底层最终都会调用ScopedProxyCreator.createScopedProxy)

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);
}

ScopedProxyCreator.java

final class ScopedProxyCreator {

    private ScopedProxyCreator() {
    }


    public static BeanDefinitionHolder createScopedProxy(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {

        return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
    }

    public static String getTargetBeanName(String originalBeanName) {
        return ScopedProxyUtils.getTargetBeanName(originalBeanName);
    }

}

ScopedProxyUtils.java

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
            BeanDefinitionRegistry registry, boolean proxyTargetClass) {

    String originalBeanName = definition.getBeanName();
    BeanDefinition targetDefinition = definition.getBeanDefinition();
    // 拼接scopedTarget.前缀
    String targetBeanName = getTargetBeanName(originalBeanName);

    // Create a scoped proxy definition for the original bean name,
    // "hiding" the target bean in an internal target definition.
    // 代理BeanDefinition
    RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
    proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
    proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
    proxyDefinition.setSource(definition.getSource());
    proxyDefinition.setRole(targetDefinition.getRole());

    proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
    if (proxyTargetClass) {
        targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
    }
    else {
        proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
    }

    // Copy autowire settings from original bean definition.
    proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
    proxyDefinition.setPrimary(targetDefinition.isPrimary());
    if (targetDefinition instanceof AbstractBeanDefinition) {
        proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
    }

    // The target bean should be ignored in favor of the scoped proxy.
    // 目标bean不能被注入
    targetDefinition.setAutowireCandidate(false);
    targetDefinition.setPrimary(false);

    // Register the target bean as separate bean in the factory.
    registry.registerBeanDefinition(targetBeanName, targetDefinition);

    // Return the scoped proxy definition as primary bean definition
    // (potentially an inner bean).
    return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
创建代理对象

代理BeanDefinition类是ScopedProxyFactoryBean,这是一个FactoryBean,它会基于ProxyFactory创建一个代理。

ProxyFactory是Spring创建代理的公共工厂,以cglib代理为例,它内部会调用CglibAopProxy的getProxy方法拿到代理类,该类设置了一个公共拦截器DynamicAdvisedInterceptor

DynamicAdvisedInterceptor.java

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

    private final AdvisedSupport advised;

    public DynamicAdvisedInterceptor(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    @Nullable
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        Object target = null;
        TargetSource targetSource = this.advised.getTargetSource();
        try {
            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
            // 获取目标对象(关键点)
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            Object retVal;
            // Check whether we only have one InvokerInterceptor: that is,
            // no real advice, but just reflective invocation of the target.
            if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
                // We can skip creating a MethodInvocation: just invoke the target directly.
                // Note that the final invoker must be an InvokerInterceptor, so we know
                // it does nothing but a reflective operation on the target, and no hot
                // swapping or fancy proxying.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = invokeMethod(target, method, argsToUse, methodProxy);
            }
            else {
                // We need to create a method invocation...
                retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
            }
            retVal = processReturnType(proxy, target, method, retVal);
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

    @Override
    public boolean equals(@Nullable Object other) {
        return (this == other ||
                (other instanceof DynamicAdvisedInterceptor &&
                        this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
    }

    /**
     * CGLIB uses this to drive proxy creation.
     */
    @Override
    public int hashCode() {
        return this.advised.hashCode();
    }
}

ScopedProxyFactoryBean创建代理时设置的TargetSourceSimpleBeanTargetSource

ScopedProxyFactoryBean.java

public class ScopedProxyFactoryBean extends ProxyConfig
        implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean {

    /** The TargetSource that manages scoping. */
    private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();

    public void setBeanFactory(BeanFactory beanFactory) {
        if (!(beanFactory instanceof ConfigurableBeanFactory)) {
                throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
        }
        ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

        this.scopedTargetSource.setBeanFactory(beanFactory);

        ProxyFactory pf = new ProxyFactory();
        pf.copyFrom(this);
        // 设置代理的TargetSource
        pf.setTargetSource(this.scopedTargetSource);

        Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
        Class<?> beanType = beanFactory.getType(this.targetBeanName);
        if (beanType == null) {
                throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
                                "': Target type could not be determined at the time of proxy creation.");
        }
        if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
                pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
        }

        // Add an introduction that implements only the methods on ScopedObject.
        ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
        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.
        pf.addInterface(AopInfrastructureBean.class);

        this.proxy = pf.getProxy(cbf.getBeanClassLoader());
    }

}

SimpleBeanTargetSource.java

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {

    @Override
    public Object getTarget() throws Exception {
        return getBeanFactory().getBean(getTargetBeanName());
    }
}

可以看到绕了这么一大圈,就是创建一个代理类,然后调用方法时会调用容器的getBean方法拿到目标对象再执行方法。

拿目标对象

目标对象的scope=refresh,因此需要对应的Scope提供get方法来获取

Scope.java(接口)

public interface Scope {

    Object get(String name, ObjectFactory<?> objectFactory);

    @Nullable
    Object remove(String name);

}

@RefreshScope注解对应的Scope实现为RefreshScope,而RefreshScope又继承了GenericScope

GenericScope.java

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
    // 往容器注册Scope,如果不注册,getBean方法会报错
    beanFactory.registerScope(this.name, this);
    setSerializationId(beanFactory);
}

@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
    // 注意这里,name存在,则返回原来的wrapper,相当于Map的putIfAbsent
    // 因此如果不删除的话拿到的一直是同一个对象
    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;
    }
}

RefreshScope.java

/**
 * 刷新, ContextRefresher.refresh方法会调用
 */
public void refreshAll() {
    super.destroy();
    this.context.publishEvent(new RefreshScopeRefreshedEvent());
}

GenericScope.java

@Override
public void destroy() {
    List<Throwable> errors = new ArrayList<Throwable>();
    // 清除缓存,下次容器中的getBean变化重新创建一个对象了
    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();
}
@ConfigurationProperties

@ConfigurationProperties标注的bean不像@RefreshScope标注的一样,它只是在环境发生变化时,重新绑定下属性,并不会重新创建新的对象示例,也没有代理对象。

ContextRefresher.java

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());
    updateEnvironment();
    Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();
    // 发布环境变化事件
    this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
    return keys;
}

ConfigurationPropertiesRebinder.java

/**
 * 监听EnvironmentChangeEvent事件
 */
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
    if (this.applicationContext.equals(event.getSource())
            || event.getKeys().equals(event.getSource())) {
        // 重新绑定属性
        rebind();
    }
}

public void rebind() {
    this.errors.clear();
    // 只包含被@ConfigurationProperties标注的类, 并且没有被@RefreshScope标记
    for (String name : this.beans.getBeanNames()) {
        rebind(name);
    }
}

public boolean rebind(String name) {
    if (!this.beans.getBeanNames().contains(name)) {
        return false;
    }
    ApplicationContext appContext = this.applicationContext;
    while (appContext != null) {
        if (appContext.containsLocalBean(name)) {
            return rebind(name, appContext);
        }
        else {
            appContext = appContext.getParent();
        }
    }
    return false;
}

private boolean rebind(String name, ApplicationContext appContext) {
    try {
        Object bean = appContext.getBean(name);
        if (AopUtils.isAopProxy(bean)) {
            // 如果被代理了,拿到目标对象
            bean = ProxyUtils.getTargetObject(bean);
        }
        if (bean != null) {
            // TODO: determine a more general approach to fix this.
            // see
            // https://github.com/spring-cloud/spring-cloud-commons/issues/571
            if (getNeverRefreshable().contains(bean.getClass().getName())) {
                return false; // ignore
            }
            // 销毁bean, 释放资源, 比如close等方法, 不会从容器中移除
            appContext.getAutowireCapableBeanFactory().destroyBean(bean);
            // 重新初始化, 注意@ConfigurationProperties逻辑是在
            // BeanPostProcessor.postProcessBeforeInitialization执行的, initializeBean包含了这一步
            appContext.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;
}
posted on 2023-10-28 16:54  wastonl  阅读(621)  评论(0编辑  收藏  举报