Spring Cloud Config 的 refresh 机

2020-01-14  本文已影响0人  蓝笔头

1. configserver 项目

通过 https://start.spring.io/ 新建 configserver 项目。

需要导入如下依赖,可以在上述网址中选择依赖组件。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

在主类中新增 @EnableConfigServer 注解。

@EnableConfigServer
@SpringBootApplication
public class ConfigserverApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigserverApplication.class, args);
    }
}

使用本地文件存储配置文件的方式,在 application.properties 文件中新增如下配置。

spring.application.name=configserver
server.port=8888
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=classpath:/config

resources 目录下新增 config 子目录,并创建 application-dev.ymlapplication-test.yml 文件,文件内容如下所示。

// application-dev.yml
name: "dev-config"

// application-test.yml
name: "test-config"

configserver 项目整体结构如图1 所示。

图1:configserver 项目结构

运行 configserver 项目。

图2:configserver 启动日志

2. config-client-demo 项目

同样可以通过 https://start.spring.io/ 新建 config-client-demo 项目。

需要导入如下依赖,可以在上述网址中选择依赖组件。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
</dependency>

application.properties 文件中新增如下配置。

# 开启所有 Actuator Endpoint
management.endpoints.web.exposure.include=*

新增 bootstrap.yml 文件,并加入 Spring Cloud Config 相关配置。

spring:
  cloud:
    config:
      uri: http://localhost:8888 # configserver 地址
      # name-profile 组合对应 configserver 中具体的配置文件,这里映射的是 application-dev.yml
      name: application
      profile: dev

新增 ConfigProperties 类存储对应的配置,并加上 @RefreshScope 用来刷新配置。

@Data
@Component
@RefreshScope
public class ConfigProperties {
    @Value("${name}")
    private String name;
}

新增 TestConfigController 用来提供测试接口。

@RestController
@RequiredArgsConstructor
@RequestMapping("/test/config")
public class TestConfigController {
    @Autowired
    private ConfigProperties configProperties;

    @Value("${name}")
    private String name;

    @GetMapping("/name")
    public String name() {
        System.out.println("------------------------");
        System.out.println(name);
        System.out.println(configProperties.getName());
        System.out.println("------------------------");
        return name;
    }
}

config-client-demo 项目整体结构如图3 所示。

图3:config-client-demo 项目结构

调用 http://localhost:8080/test/config/name 接口,打印结果如下所示。

image.png

3. 修改配置

修改 configserver 中的 application-dev.yml 配置,并重启 configserver 服务。

name: "dev-config-update"

config-client-demo 中调用 http://localhost:8080/actuator/refresh 接口用来刷新配置,然后调用 http://localhost:8080/test/config/name 接口,打印结果如下所示。

image.png

可以发现,有 @RefreshScope 注解的配置被刷新了,否则配置没有被刷新。

4. 源码解读

4.1 refresh 接口

下面我们从 RefreshEndpoint 类开始跟踪。

@Endpoint(id = "refresh")
public class RefreshEndpoint {
    private ContextRefresher contextRefresher;

    // http://localhost:8080/actuator/refresh 接口执行逻辑
    public Collection<String> refresh() {
        Set<String> keys = this.contextRefresher.refresh();
        return keys;
    }
}

后续调用链路为:

4.2 GenericScope 类

public class GenericScope implements Scope, BeanFactoryPostProcessor,
        BeanDefinitionRegistryPostProcessor, DisposableBean {

    /**
     * Prefix for the scoped target.
     */
    public static final String SCOPED_TARGET_PREFIX = "scopedTarget.";

    private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
            new StandardScopeCache());

    // 清空 Scope 中的缓存对象,这里指 RefreshScope
    public void destroy() {
        Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
        for (BeanLifecycleWrapper wrapper : wrappers) {
                wrapper.destroy();
        }
    }

    // 获取 name 在 Scope 中的 Spring Bean
    public Object get(String name, ObjectFactory<?> objectFactory) {
        // 底层调用的是 putIfAbsent 方法
        // 如果 cache 存在对应的 name 的 BeanLifecycleWrapper 则直接返回,否则添加
        BeanLifecycleWrapper value = this.cache.put(name,
                new BeanLifecycleWrapper(name, objectFactory));
        try {
            return value.getBean();
        }
    }

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
            throws BeansException {
        for (String name : registry.getBeanDefinitionNames()) {
            BeanDefinition definition = registry.getBeanDefinition(name);
            if (definition instanceof RootBeanDefinition) {
                RootBeanDefinition root = (RootBeanDefinition) definition;
                if (root.getDecoratedDefinition() != null && root.hasBeanClass()
                        && root.getBeanClass() == ScopedProxyFactoryBean.class) {
                    if (getName().equals(root.getDecoratedDefinition().getBeanDefinition()
                            .getScope())) {
                        // 把 ScopedProxyFactoryBean 替换为 LockedScopedProxyFactoryBean
                        root.setBeanClass(LockedScopedProxyFactoryBean.class);
                        root.getConstructorArgumentValues().addGenericArgumentValue(this);
                        root.setSynthetic(true);
                    }
                }
            }
        }
    }

    private static class BeanLifecycleWrapper {

        private final String name;

        private final ObjectFactory<?> objectFactory;

        private Object bean;

        // 通过 objectFactory 获取 Spring Bean 对象,并缓存在 bean 字段中
        public Object getBean() {
            if (this.bean == null) {
                synchronized (this.name) {
                    if (this.bean == null) {
                        this.bean = this.objectFactory.getObject();
                    }
                }
            }
            return this.bean;
        }
    }
}

4.2 ScopedProxyUtils 类

public abstract class ScopedProxyUtils {

    private static final String TARGET_NAME_PREFIX = "scopedTarget.";

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

        String originalBeanName = definition.getBeanName();
        BeanDefinition targetDefinition = definition.getBeanDefinition();
        String targetBeanName = getTargetBeanName(originalBeanName);

        // 为 originalBeanName 创建一个 scoped proxy 的 RootBeanDefinition
        RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
        proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
        proxyDefinition.setSource(definition.getSource());
        proxyDefinition.setRole(targetDefinition.getRole());

        // 给 ScopedProxyFactoryBean 中的 targetBeanName 字段赋值
        proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);

        // 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.
        targetDefinition.setAutowireCandidate(false);
        targetDefinition.setPrimary(false);

    
        // targetBeanName 关联目标 bean,注册到 Spring 容器中
        registry.registerBeanDefinition(targetBeanName, targetDefinition);

        // 返回 originalBeanName 关联的 scoped proxy definition
        return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }

    /**
     * 生成目标bean的名称
     *
     *      originalBeanName 是 scoped proxy 的名称(代理类)
     *      targetBeanName 是 scoped proxy 持有的目标bean 的名称(被代理类)
     */
    // 
    public static String getTargetBeanName(String originalBeanName) {
        return TARGET_NAME_PREFIX + originalBeanName;
    }

}

生成 scoped proxy 的调用链路:

4.3 ScopedProxyFactoryBean 类

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

    // 从 Spring 容器中通过 getBean() 方法获取 TargetSource
    private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();

    // ScopedProxyUtils 中的 getTargetBeanName() 方法生成
    private String targetBeanName;

    // 代理对象的缓存
    private Object proxy;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

        this.scopedTargetSource.setBeanFactory(beanFactory);

        // 通过 ProxyFactory 生成代理对象
        ProxyFactory pf = new ProxyFactory();
        pf.copyFrom(this);
        // 设置代理对象的 TargetSource 为 scopedTargetSource (SimpleBeanTargetSource)
        pf.setTargetSource(this.scopedTargetSource);


        Class<?> beanType = beanFactory.getType(this.targetBeanName);
        if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
            pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
        }

        ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
        pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

        pf.addInterface(AopInfrastructureBean.class);

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


    public Object getObject() {
        if (this.proxy == null) {
            throw new FactoryBeanNotInitializedException();
        }
        return this.proxy;
    }

}

public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {

    // 在代理方法中获取目标对象
    public Object getTarget() throws Exception {
        // 直接从 Spring 容器中获取
        return getBeanFactory().getBean(getTargetBeanName());
    }

}

LockedScopedProxyFactoryBeanGenericScope 的内部类。

public static class LockedScopedProxyFactoryBean<S extends GenericScope>
        extends ScopedProxyFactoryBean implements MethodInterceptor {

    private final S scope;

    private String targetBeanName;

    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        Object proxy = getObject();
        if (proxy instanceof Advised) {
            // 把当前 MethodInterceptor 放到 Advice 的首位
            // 即让代理方法执行时,先执行此 MethodInterceptor 的 invoke 方法
            Advised advised = (Advised) proxy;
            advised.addAdvice(0, this);
        }
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
                || AopUtils.isHashCodeMethod(method)
                || isScopedObjectGetTargetObject(method)) {
            return invocation.proceed();
        }
        Object proxy = getObject();
        try {
            if (proxy instanceof Advised) {
                Advised advised = (Advised) proxy;
                ReflectionUtils.makeAccessible(method);
                return ReflectionUtils.invokeMethod(method,
                        advised.getTargetSource().getTarget(), // 调用 SimpleBeanTargetSource 的 getTarget() 从 Spring 容器获取 Bean
                        invocation.getArguments());
            }
            return invocation.proceed();
        }
    }

}

5. 配置刷新分析

再次贴出 TestConfigController 的代码。

@RestController
@RequiredArgsConstructor
@RequestMapping("/test/config")
public class TestConfigController {
    @Autowired
    private ConfigProperties configProperties;

    @Value("${name}")
    private String name;

    @GetMapping("/name")
    public String name() {

        System.out.println("------------------------");
        System.out.println(name);
        System.out.println(configProperties.getName());
        System.out.println("------------------------");
        return name;
    }
}

debug 结果如下所示。

image.png

从上图可知,configProperties 是一个 CglibAopProxy 代理类,因此调用其 getName() 方法会进入 DynamicAdvisedInterceptorintercept() 方法。

调用链路如下所示:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {


    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;


        // Create bean instance.
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
                try {
                    return createBean(beanName, mbd, args);
                }
            });
            bean = 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);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        }

        else {
            String scopeName = mbd.getScope();
            // 获取到 RefreshScope
            final Scope scope = this.scopes.get(scopeName);
            try {
                // 调用 RefreshScope 父类 GenericScope 的 get() 方法
                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;
    }
}

通过 4.1 refresh 接口 可以知道在调用 /refresh 接口后,会触发 GenericScopedestroy() 逻辑,从而清空缓存。

之后, GenericScopeget() 方法会重新通过 objectFactorycreateBean() 生成新的对象。

上一篇下一篇

猜你喜欢

热点阅读