美文网首页
Spring Cloud Config 的 refresh 机

Spring Cloud Config 的 refresh 机

作者: 蓝笔头 | 来源:发表于2020-01-14 19:52 被阅读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;
        }
    }
    

    后续调用链路为:

    • ContextRefresherrefresh()
    • RefreshScoperefreshAll()
    • GenericScopedestroy()

    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 的调用链路:

    • AbstractApplicationContext 类中的 refresh() 方法
    • AbstractApplicationContext 类中的 invokeBeanFactoryPostProcessors() 方法
    • PostProcessorRegistrationDelegate 类中的 invokeBeanFactoryPostProcessors() 方法
    • PostProcessorRegistrationDelegate 类中的 invokeBeanDefinitionRegistryPostProcessors() 方法
    • ConfigurationClassPostProcessor 类中的 postProcessBeanDefinitionRegistry() 方法
    • ConfigurationClassPostProcessor 类中的 processConfigBeanDefinitions() 方法
    • ConfigurationClassParser 类中的 parse() 方法
    • ConfigurationClassParser 类中的 processConfigurationClass() 方法
    • ComponentScanAnnotationParser 类中的 parse() 方法
    • ClassPathBeanDefinitionScanner 类中的 doScan() 方法
    • AnnotationConfigUtils 类中的 applyScopedProxyMode() 方法
    • ScopedProxyCreator 类中的 createScopedProxy() 方法
    • ScopedProxyUtils 类中的 createScopedProxy() 方法

    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() 方法。

    调用链路如下所示:

    • DynamicAdvisedInterceptor 类的 intercept() 方法。
    • LockedScopedProxyFactoryBean 类的 invoke() 方法。
    • AbstractBeanFactory 类的 getBean() 方法,通过 ScopedProxyUtils 中的 getTargetBeanName() 方法生成的 name
    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() 生成新的对象。

    相关文章

      网友评论

          本文标题:Spring Cloud Config 的 refresh 机

          本文链接:https://www.haomeiwen.com/subject/kjlaactx.html