亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot @Value注解支持配置自動刷新能力擴展方式

 更新時間:2024年12月10日 09:02:06   作者:程序員高級碼農(nóng).  
本文介紹了如何通過自定義注解和BeanPostProcessor實現(xiàn)SpringBoot中@Value注解的配置自動刷新能力,主要步驟包括:定義一個支持動態(tài)刷新的注解,實現(xiàn)配置的動態(tài)變更,以及通過BeanPostProcessor掃描并刷新使用@Value注解的變量

在我們的日常開發(fā)中,使用@Value來綁定配置屬于非常常見的基礎(chǔ)操作,但是這個配置注入是一次性的,簡單來說就是配置一旦賦值,則不會再修改;通常來講,這個并沒有什么問題,基礎(chǔ)的 SpringBoot 項目的配置也基本不存在配置變更,如果有使用過 SpringCloudConfig 的小伙伴,會知道@Value可以綁定遠程配置,并支持動態(tài)刷新

接下來本文將通過一個實例來演示下,如何讓@Value注解支持配置刷新;本文將涉及到以下知識點

  • BeanPostProcessorAdapter + 自定義注解:獲取支持自動刷新的配置類
  • MapPropertySource:實現(xiàn)配置動態(tài)變更

項目環(huán)境

項目依賴

本項目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA進行開發(fā)

開一個 web 服務(wù)用于測試

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

配置動態(tài)刷新支持

1. 思路介紹

要支持配合的動態(tài)刷新,重點在于下面兩點

  • 如何修改Environment中的配置源
  • 配置變更之后,如何通知到相關(guān)的類同步更新

2. 修改配置

相信很多小伙伴都不會去修改Environment中的數(shù)據(jù)源,突然冒出一個讓我來修改配置源的數(shù)據(jù),還是有點懵的,這里推薦之前分享過一篇博文 SpringBoot 基礎(chǔ)篇之自定義配置源的使用姿勢

當我們知道如何去自定義配置源之后,再來修改數(shù)據(jù)源,就會有一點思路了

定義一個配置文件application-dynamic.yml

xhh:
  dynamic:
    name: 一灰灰blog

然后在主配置文件中使用它

spring:
  profiles:
    active: dynamic

使用配置的 java config

@Data
@Component
public class RefreshConfigProperties {

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

    @Value("${xhh.dynamic.age:18}")
    private Integer age;

    @Value("hello ${xhh.dynamic.other:test}")
    private String other;
}

接下來進入修改配置的正題

@Autowired
ConfigurableEnvironment environment;

// --- 配置修改
String name = "applicationConfig: [classpath:/application-dynamic.yml]";
MapPropertySource propertySource = (MapPropertySource) environment.getPropertySources().get(name);
Map<String, Object> source = propertySource.getSource();
Map<String, Object> map = new HashMap<>(source.size());
map.putAll(source);
map.put(key, value);
environment.getPropertySources().replace(name, new MapPropertySource(name, map));

上面的實現(xiàn)中,有幾個疑問點

name 如何找到的?

  • debug...

配置變更

  • 注意修改配置是新建了一個 Map,然后將舊的配置拷貝到新的 Map,然后再執(zhí)行替換;并不能直接進行修改,有興趣的小伙伴可以實測一下為什么

3. 配置同步

上面雖然是實現(xiàn)了配置的修改,但是對于使用@Value注解修飾的變量,已經(jīng)被賦值了,如何能感知到配置的變更,并同步刷新呢?

這里就又可以拆分兩塊

找到需要修改的配置

修改事件同步

3.1 找出需要刷新的配置變量

我們這里額外增加了一個注解,用來修飾需要支持動態(tài)刷新的場景

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RefreshValue {
}

接下來我們就是找出有上面這個注解的類,然后支持這些類中@Value注解綁定的變量動態(tài)刷新

關(guān)于這個就有很多實現(xiàn)方式了,我們這里選擇BeanPostProcessor,bean 創(chuàng)建完畢之后,借助反射來獲取@Value綁定的變量,并緩存起來

@Component
public class AnoValueRefreshPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements EnvironmentAware {
    private Map<String, List<FieldPair>> mapper = new HashMap<>();
    private Environment environment;

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        processMetaValue(bean);
        return super.postProcessAfterInstantiation(bean, beanName);
    }

    /**
     * 這里主要的目的就是獲取支持動態(tài)刷新的配置屬性,然后緩存起來
     *
     * @param bean
     */
    private void processMetaValue(Object bean) {
        Class clz = bean.getClass();
        if (!clz.isAnnotationPresent(RefreshValue.class)) {
            return;
        }

        try {
            for (Field field : clz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Value.class)) {
                    Value val = field.getAnnotation(Value.class);
                    List<String> keyList = pickPropertyKey(val.value(), 0);
                    for (String key : keyList) {
                        mapper.computeIfAbsent(key, (k) -> new ArrayList<>())
                                .add(new FieldPair(bean, field, val.value()));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    /**
     * 實現(xiàn)一個基礎(chǔ)的配置文件參數(shù)動態(tài)刷新支持
     *
     * @param value
     * @return 提取key列表
     */
    private List<String> pickPropertyKey(String value, int begin) {
        int start = value.indexOf("${", begin) + 2;
        if (start < 2) {
            return new ArrayList<>();
        }

        int middle = value.indexOf(":", start);
        int end = value.indexOf("}", start);

        String key;
        if (middle > 0 && middle < end) {
            // 包含默認值
            key = value.substring(start, middle);
        } else {
            // 不包含默認值
            key = value.substring(start, end);
        }

        List<String> keys = pickPropertyKey(value, end);
        keys.add(key);
        return keys;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class FieldPair {
        private static PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}",
                ":", true);

        Object bean;
        Field field;
        String value;

        public void updateValue(Environment environment) {
            boolean access = field.isAccessible();
            if (!access) {
                field.setAccessible(true);
            }

            String updateVal = propertyPlaceholderHelper.replacePlaceholders(value, environment::getProperty);
            try {
                if (field.getType() == String.class) {
                    field.set(bean, updateVal);
                } else {
                    field.set(bean, JSONObject.parseObject(updateVal, field.getType()));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            field.setAccessible(access);
        }
    }
}

上面的實現(xiàn)雖然有點長,但是核心邏輯就下面節(jié)點

processMetaValue()

  • 通過反射
  • 撈取帶有@Value注解的變量

pickPropertyKey()

  • 主要就是解析@Value注解中表達式,挑出變量名,用于緩存
  • 如: @value("hello ${name:xhh} ${now:111}
  • 解析之后,有兩個變量,一個 name 一個 now
  • 緩存Map<String, List<FieldPair>>
  • 緩存的 key,為變量名
  • 緩存的 value,自定義類,主要用于反射修改配置值

3.2 修改事件同步

從命名也可以看出,我們這里選擇事件機制來實現(xiàn)同步,直接借助 Spring Event 來完成

一個簡單的自定義類事件類

public static class ConfigUpdateEvent extends ApplicationEvent {
    String key;

    public ConfigUpdateEvent(Object source, String key) {
        super(source);
        this.key = key;
    }
}

消費也比較簡單,直接將下面這段代碼,放在上面的AnoValueRefreshPostProcessor, 接收到變更事件,通過 key 從緩存中找到需要變更的 Field,然后依次執(zhí)行刷新即可

@EventListener
public void updateConfig(ConfigUpdateEvent configUpdateEvent) {
    List<FieldPair> list = mapper.get(configUpdateEvent.key);
    if (!CollectionUtils.isEmpty(list)) {
        list.forEach(f -> f.updateValue(environment));
    }
}

實例演示

最后將前面修改配置的代碼塊封裝一下,提供一個接口,來驗證下我們的配置刷新

@RestController
public class DynamicRest {
    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    ConfigurableEnvironment environment;
    @Autowired
    RefreshConfigProperties refreshConfigProperties;

    @GetMapping(path = "dynamic/update")
    public RefreshConfigProperties updateEnvironment(String key, String value) {
        String name = "applicationConfig: [classpath:/application-dynamic.yml]";
        MapPropertySource propertySource = (MapPropertySource) environment.getPropertySources().get(name);
        Map<String, Object> source = propertySource.getSource();
        Map<String, Object> map = new HashMap<>(source.size());
        map.putAll(source);
        map.put(key, value);
        environment.getPropertySources().replace(name, new MapPropertySource(name, map));

        applicationContext.publishEvent(new AnoValueRefreshPostProcessor.ConfigUpdateEvent(this, key));
        return refreshConfigProperties;
    }
}

總結(jié)

本文主要通過簡單的幾步,對@Value進行了拓展,支持配置動態(tài)刷新,核心知識點下面三塊:

  • 使用 BeanPostProcess 來掃描需要刷新的變量
  • 利用 Spring Event 事件機制來實現(xiàn)刷新同步感知
  • 至于配置的修改,則主要是MapPropertySource來實現(xiàn)配置的替換修改

請注意,上面的這個實現(xiàn)思路,與 Spring Cloud Config 是有差異的。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Jackson將json string轉(zhuǎn)為Object,org.json讀取json數(shù)組的實例

    Jackson將json string轉(zhuǎn)為Object,org.json讀取json數(shù)組的實例

    下面小編就為大家?guī)硪黄狫ackson將json string轉(zhuǎn)為Object,org.json讀取json數(shù)組的實例,具有很好的參考價值,希望對大家有所幫助
    2017-12-12
  • java實現(xiàn)查找PDF關(guān)鍵字所在頁碼及其坐標

    java實現(xiàn)查找PDF關(guān)鍵字所在頁碼及其坐標

    這篇文章主要介紹了java實現(xiàn)查找PDF關(guān)鍵字所在頁碼及其坐標的方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • SpringBoot項目中@RestControllerAdvice全局異常失效問題的解決

    SpringBoot項目中@RestControllerAdvice全局異常失效問題的解決

    @RestController注解是一個用于定義RESTful Web服務(wù)的控制器的特殊注解,它是@Controller和@ResponseBody注解的結(jié)合體,意味著你不需要在每個處理請求的方法上都添加@ResponseBody,本文給大家介紹了解決SpringBoot項目中@RestControllerAdvice全局異常失效問題
    2024-11-11
  • Spring Boot web項目的TDD流程

    Spring Boot web項目的TDD流程

    TDD(Test-driven development) 測試驅(qū)動開發(fā),簡單點說就是編寫測試,再編寫代碼。這是首要一條,不可動搖的一條,先寫代碼后寫測試的都是假TDD。
    2021-05-05
  • 一篇文章帶你入門java網(wǎng)絡(luò)編程

    一篇文章帶你入門java網(wǎng)絡(luò)編程

    網(wǎng)絡(luò)編程是指編寫運行在多個設(shè)備(計算機)的程序,這些設(shè)備都通過網(wǎng)絡(luò)連接起來。本文介紹了一些網(wǎng)絡(luò)編程基礎(chǔ)的概念,并用Java來實現(xiàn)TCP和UDP的Socket的編程,來讓讀者更好的了解其原理
    2021-08-08
  • Java反轉(zhuǎn)字符串的10種方法

    Java反轉(zhuǎn)字符串的10種方法

    這篇文章主要介紹了Java反轉(zhuǎn)字符串的10種方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,下面我們來一起學(xué)習(xí)一下吧
    2019-06-06
  • Spring Cloud CLI簡單介紹

    Spring Cloud CLI簡單介紹

    本文我們將介紹Spring Boot Cloud CLI(或簡稱Cloud CLI)。該工具為Spring Boot CLI提供了一組命令行增強功能,有助于進一步抽象和簡化Spring Cloud部署。感興趣的小伙伴們可以參考一下
    2018-12-12
  • IntelliJ IDEA下Maven創(chuàng)建Scala項目的方法步驟

    IntelliJ IDEA下Maven創(chuàng)建Scala項目的方法步驟

    這篇文章主要介紹了IntelliJ IDEA下Maven創(chuàng)建Scala項目的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • String與Blob互轉(zhuǎn)和file文件與Blob互轉(zhuǎn)方式

    String與Blob互轉(zhuǎn)和file文件與Blob互轉(zhuǎn)方式

    這篇文章主要介紹了String與Blob互轉(zhuǎn)和file文件與Blob互轉(zhuǎn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 基于Java?Agent的premain方式實現(xiàn)方法耗時監(jiān)控問題

    基于Java?Agent的premain方式實現(xiàn)方法耗時監(jiān)控問題

    javaagent是在JDK5之后提供的新特性,也可以叫java代理,這篇文章主要介紹了基于Java?Agent的premain方式實現(xiàn)方法耗時監(jiān)控問題,需要的朋友可以參考下
    2022-10-10

最新評論