Springboot @Value 单节点动态刷新

2021-04-23 11:06:01 浏览数 (2)

1, 前面我们写了使用自定义的加载properties 的代码

里面实现了 EnvironmentPostProcessor

代码语言:javascript复制
@Override
代码语言:javascript复制
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    environment.getPropertySources().addLast(new MapPropertySource(FILE_PATH, mapPropertySource()));
}

也就是说我们手动实现这个也就可以添加环境变量, 由于是MapPropertySource , 相同的key 会覆盖。

代码。

下面我们开看刷新Environment 的代码实现

代码语言:javascript复制
    public static void refreshEnvironment(Map<String, Object> map,ConfigurableEnvironment configurableEnvironment) {
        MutablePropertySources mutablePropertySources = configurableEnvironment.getPropertySources();
        // 添加远程配置信息
        mutablePropertySources.addFirst(new MapPropertySource("remoteConfig", map));
    }

刷新玩Environment ,在刷新Bean 对象,利用field 属性通过反射

有两个值, 一个是Bean 对象,一个是ConfigurablePropertyResolver 配置文件参数分解器。

获取bean 的field 的属性看上面有没有@Value注解,根据注解的值和field的type 获取field 的值在从新的赋值。

具体代码

代码语言:javascript复制
public static void refreshBean(Object bean, ConfigurablePropertyResolver propertyResolver) {
        // 定义EL表达式解释器
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        TemplateParserContext templateParserContext= new TemplateParserContext();

        String keyResolver, valueResolver = null;
        Object parserValue;
         // 获取真实对象属性

        Field[] declaredFields = bean.getClass().getDeclaredFields();
        boolean cglib = Arrays.stream(declaredFields).anyMatch(x -> x.getName().contains("CGLIB"));
        // 如果是cglib 代理找其父类
        if(cglib){
            declaredFields = bean.getClass().getSuperclass().getDeclaredFields();
        }

        // 遍历Bean实例所有属性
        for (Field field : declaredFields) {
            // 判断field是否含有@Value注解
            if (field.isAnnotationPresent(Value.class)) {
                // 读取Value注解占位符
                keyResolver = field.getAnnotation(Value.class).value();
                try {
                    // 读取属性值
                    valueResolver = propertyResolver.resolveRequiredPlaceholders(keyResolver);
                    // EL表达式解析
                    // 兼容形如:@Value("#{'${codest.five.url}'.split(',')}")含有EL表达式的情况
                    Expression expression = spelExpressionParser.parseExpression(valueResolver, templateParserContext);
                    if(field.getType() == Boolean.class){
                        parserValue =Boolean.valueOf(expression.getValue().toString());
                    }
                    else if(field.getType() == Integer.class){
                        parserValue =Integer.valueOf(expression.getValue().toString());
                    }
                    else if(field.getType() == Long.class){
                        parserValue =Long.valueOf(expression.getValue().toString());
                    }else {
                        parserValue = expression.getValue(field.getType());

                    }

                } catch (IllegalArgumentException e) {
                    logger.warn("{}", e.getMessage());
                    continue;
                }
                // 判断配置项是否存在
                if (Objects.nonNull(valueResolver)) {
                    field.setAccessible(true);
                    try {
                            field.set(bean, parserValue);
                            continue;
                    } catch (IllegalAccessException e) {
                        logger.error("{}刷新属性值出错, bean: [{}], field: [{}], value: [{}]", bean.getClass().getName(), field.getName(), valueResolver);
                    }
                }
            }
        }
    }

SpringBean 工具类

代码语言:javascript复制
代码语言:javascript复制
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

}

测试代码

代码语言:javascript复制
@Component
public class Person {

    @Value("${person.isopen:false}")
    private Boolean isopen;

    // 通过反射,必须有get set 
    public Boolean getIsopen() {
        return isopen;
    }

    public void setIsopen(Boolean isopen) {
        this.isopen = isopen;
    }
}

    @Autowired
    private ConfigurablePropertyResolver configurablePropertyResolver;

    @Autowired
    private ConfigurableEnvironment configurableEnvironment;


    @Autowired
    private Person person;
    /**
     *  动态刷新参数接口
     * @param map
     * @return Map<String, Object>
     */
    @PostMapping("private/reSetProperties")
    public Map<String, Object> reSetProperties(@RequestBody Map<String, Object> map)  {
        System.out.println(" 改变之前 " person.getIsopen());
        String name = String.valueOf(map.get("beanName"));
        map.remove("beanName");
        SpringUtils.refreshEnvironment(map,configurableEnvironment);
        Object bean = SpringContextUtil.getBean(name);
        SpringUtils.refreshBean(bean,configurablePropertyResolver);
        System.out.println(" 改变之后 " person.getIsopen());
        return Response.customSuccessResponse(" 刷新成功");
    }

postman 测试

结果

完美,喜欢,点赞转发

0 人点赞