springboot三行代码实现配置项的动态刷新(无配置中心)

springboot单体项目中在没有引入nacos等配置中心的情况下,想要动态的更改某些配置项,可以通过自己代码实现这个功能。

上菜

springboot三行代码实现配置项的动态刷新(无配置中心)_第1张图片比如:在上面的配置文件中,有个version的配置,当我想动态的更改这个name的值的时候,如果在代码里更改然后打包也是可行的方法,但是如果更改频繁,那么就麻烦了。

方案演示,实际以个人情况为准:

    @PostMapping("/v2/config/set")
    @ApiOperation(value = "自定义动态刷新配置", notes = "自定义动态刷新配置")
    public R versionSet(@Valid @RequestBody Version version1) {
        // 设置需要动态配置的配置项
        Map map = new HashMap<>();
        map.put("version.name", version1.getName());
        MapPropertySource mapPropertySource = new MapPropertySource("version", map);
        // 把自定义作用域注册到当前容器
        String[] registeredScopeNames = applicationContext.getBeanFactory().getRegisteredScopeNames();
        applicationContext.getBeanFactory().registerScope(ConfigRefreshScope.SCOPE_REFRESH, ConfigRefreshScope.getInstance());
        // 把修改的配置优先级设置最高
        applicationContext.getEnvironment().getPropertySources().addFirst(mapPropertySource);
        // 清除容器中之前的配置bean
        ConfigRefreshScope.clean();
        String name = versionConfig.getName();
        return R.data(name);
    }

上面是动态更改配置的接口代码。 

 整体思路:1、自定义一个scope作用域类,自定义一个相关的作用域刷新注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Scope(ConfigRefreshScope.SCOPE_REFRESH)
@Documented
public @interface MyRefreshScope {

    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}

关键点: ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;这个源码自行了解下。其实就是个bean的代理,自定义的Scope中proxyMode=ScopedProxyMode.TARGET_CLASS的时候,会给这个bean创建一个代理,调用代理对象的任何方法,都会调用这个自定义的作用域实现类中get方法来重新来获取这个bean对象。

public class ConfigRefreshScope implements Scope {


    public static final String SCOPE_REFRESH = "config";

    private static final ConfigRefreshScope INSTANCE = new ConfigRefreshScope();

    // 缓存bean
    private ConcurrentHashMap beanMap = new ConcurrentHashMap<>();

    private ConfigRefreshScope() {
    }

    public static ConfigRefreshScope getInstance() {
        return INSTANCE;
    }

    /**
     * 清理当前
     */
    public static void clean() {
        INSTANCE.beanMap.clear();
    }

    @Override
    public Object get(String name, ObjectFactory objectFactory) {
        Object bean = beanMap.get(name);
        if (bean == null) {
            bean = objectFactory.getObject();
            beanMap.put(name, bean);
        }
        return bean;
    }

    @Override
    public Object remove(String s) {
        return null;
    }

    @Override
    public void registerDestructionCallback(String s, Runnable runnable) {

    }

    @Override
    public Object resolveContextualObject(String s) {
        return null;
    }

    @Override
    public String getConversationId() {
        return null;
    }
}

2、再来个配置类作为演示

@MyRefreshScope
@ConfigurationProperties(prefix = "version")
@Component
@Data
public class VersionConfig {


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

@ConfigurationProperties(prefix = "version")这是springboot的注解,不了解的麻烦五十大板,这个类上加了我们自定义的动态刷新的注解,预期目的:当刷新这个配置项的时候,这个配置类的配置的值也会改变。下面测试下。

    @GetMapping("/v2/config/get")
    public R versionGet() {
        Scope registeredScope = applicationContext.getBeanFactory().getRegisteredScope(ConfigRefreshScope.SCOPE_REFRESH);
        if(null == registeredScope) {
            // 不存在就创建
            applicationContext.getBeanFactory().registerScope(ConfigRefreshScope.SCOPE_REFRESH, ConfigRefreshScope.getInstance());
        }
        String property = applicationContext.getEnvironment().getProperty("version.name");

        return R.data("【容器中environment中的值:"+property +"】|||||"+"【容器中配置项的值:"+versionConfig.getName()+"】");
    }

上面是测试的接口代码。

汇总下整体思路:配置更改,就让spring重启初始化一下这个bean,如何重新初始化?用到容器和自定义作用域,配合具体实现类,进行配置项在容器和环境中的动态更改。

结果:

springboot三行代码实现配置项的动态刷新(无配置中心)_第2张图片

 一开始,spring容器环境中的值和容器中配置项(配置类)的值,当然了,容器中的配置项的值就是从环境中取得,此处分开写是更明了一些。

更新配置:

springboot三行代码实现配置项的动态刷新(无配置中心)_第3张图片

 更新了,再查下

springboot三行代码实现配置项的动态刷新(无配置中心)_第4张图片

 动态的更改了,不用重新启动项目。

你可能感兴趣的:(springboot,java,spring,boot,java,spring)