<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      SpringBoot動態更新yml文件

      前言

      在系統運行過程中,可能由于一些配置項的簡單變動需要重新打包啟停項目,這對于在運行中的項目會造成數據丟失,客戶操作無響應等情況發生,針對這類情況對開發框架進行升級提供yml文件實時修改更新功能

      項目依賴

      項目基于的是2.0.0.RELEASE版本,所以snakeyaml需要單獨引入,高版本已包含在內

              <dependency>
                  <groupId>org.yaml</groupId>
                  <artifactId>snakeyaml</artifactId>
                  <version>1.23</version>
              </dependency>
      

      網上大多數方法是引入spring-cloud-context配置組件調用ContextRefresher的refresh方法達到同樣的效果,考慮以下兩點未使用

      • 開發框架使用了logback日志,引入spring-cloud-context會造成日志配置讀取錯誤
      • 引入spring-cloud-context會同時引入spring-boot-starter-actuator組件,會開放一些健康檢查路由及端口,需要對框架安全方面進行額外控制

      YML文件內容獲取

      讀取resource文件下的文件需要使用ClassPathResource獲取InputStream

          public String getTotalYamlFileContent() throws Exception {
              String fileName = "application.yml";
              return getYamlFileContent(fileName);
          }
          public String getYamlFileContent(String fileName) throws Exception {
              ClassPathResource classPathResource = new ClassPathResource(fileName);
              return onvertStreamToString(classPathResource.getInputStream());
          }
          public static String convertStreamToString(InputStream inputStream) throws Exception{
             return IOUtils.toString(inputStream, "utf-8");
          }
      

      YML文件內容更新

      我們獲取到yml文件內容后可視化顯示到前臺進行展示修改,將修改后的內容通過yaml.load方法轉換成Map結構,再使用yaml.dumpAsMap轉換為流寫入到文件

          public void updateTotalYamlFileContent(String content) throws Exception {
              String fileName = "application.yml";
              updateYamlFileContent(fileName, content);
          }
      	public void updateYamlFileContent(String fileName, String content) throws Exception {
              Yaml template = new Yaml();
              Map<String, Object> yamlMap = template.load(content);
      
              ClassPathResource classPathResource = new ClassPathResource(fileName);
      
              Yaml yaml = new Yaml();
              //字符輸出
              FileWriter fileWriter = new FileWriter(classPathResource.getFile());
              //用yaml方法把map結構格式化為yaml文件結構
              fileWriter.write(yaml.dumpAsMap(yamlMap));
              //刷新
              fileWriter.flush();
              //關閉流
              fileWriter.close();
          }
      

      YML屬性刷新

      yml屬性在程序中讀取使用一般有三種

      • 使用Value注解
          @Value("${system.systemName}")
          private String systemName;
      
      • 通過enviroment注入讀取
          @Autowired
          private Environment environment;
          
          environment.getProperty("system.systemName")
      
      • 使用ConfigurationProperties注解讀取
      @Component
      @ConfigurationProperties(prefix = "system")
      public class SystemConfig {
          private String systemName;
      }
      

      Property刷新

      我們通過environment.getProperty方法讀取的配置集合實際是存儲在PropertySources中的,我們只需要把鍵值對全部取出存儲在propertyMap中,將更新后的yml文件內容轉換成相同格式的ymlMap,兩個Map進行合并,調用PropertySources的replace方法進行整體替換即可

      但是yaml.load后的ymlMap和PropertySources取出的propertyMap兩者數據解構是不同的,需要進行手動轉換

      propertyMap集合就是單純的key,value鍵值對,key是properties形式的名稱,例如system.systemName=>xxxxx集團管理系統

      ymlMap集合是key,LinkedHashMap的嵌套層次結構,例如system=>(systemName=>xxxxx集團管理系統)

      • 轉換方法如下
        public HashMap<String, Object> convertYmlMapToPropertyMap(Map<String, Object> yamlMap) {
              HashMap<String, Object> propertyMap = new HashMap<String, Object>();
              for (String key : yamlMap.keySet()) {
                  String keyName = key;
                  Object value = yamlMap.get(key);
                  if (value != null && value.getClass() == LinkedHashMap.class) {
                      convertYmlMapToPropertyMapSub(keyName, ((LinkedHashMap<String, Object>) value), propertyMap);
                  } else {
                      propertyMap.put(keyName, value);
                  }
              }
              return propertyMap;
          }
      
          private void convertYmlMapToPropertyMapSub(String keyName, LinkedHashMap<String, Object> submMap, Map<String, Object> propertyMap) {
              for (String key : submMap.keySet()) {
                  String newKey = keyName + "." + key;
                  Object value = submMap.get(key);
                  if (value != null && value.getClass() == LinkedHashMap.class) {
                      convertYmlMapToPropertyMapSub(newKey, ((LinkedHashMap<String, Object>) value), propertyMap);
                  } else {
                      propertyMap.put(newKey, value);
                  }
              }
          }
      
      • 刷新方法如下
              String name = "applicationConfig: [classpath:/" + fileName + "]";
              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<String, Object> propertyMap = convertYmlMapToPropertyMap(yamlMap);
      
              for (String key : propertyMap.keySet()) {
                  Object value = propertyMap.get(key);
                  map.put(key, value);
              }
              environment.getPropertySources().replace(name, new MapPropertySource(name, map));
      

      注解刷新

      不論是Value注解還是ConfigurationProperties注解,實際都是通過注入Bean對象的屬性方法使用的,我們先自定注解RefreshValue來修飾屬性所在Bean的class

      通過實現InstantiationAwareBeanPostProcessorAdapter接口在系統啟動時過濾篩選對應的Bean存儲下來,在更新yml文件時通過spring的event通知更新對應

      bean的屬性即可

      • 注冊事件使用EventListener注解
          @EventListener
          public void updateConfig(ConfigUpdateEvent configUpdateEvent) {
              if(mapper.containsKey(configUpdateEvent.key)){
                  List<FieldPair> fieldPairList = mapper.get(configUpdateEvent.key);
                  if(fieldPairList.size()>0){
                      for (FieldPair fieldPair:fieldPairList) {
                          fieldPair.updateValue(environment);
                      }
                  }
              }
          }
      
      • 通知觸發事件使用ApplicationContext的publishEvent方法
          @Autowired
          private ApplicationContext applicationContext;
          
        	for (String key : propertyMap.keySet()) {
             applicationContext.publishEvent(new YamlConfigRefreshPostProcessor.ConfigUpdateEvent(this, key));
          }
      

      YamlConfigRefreshPostProcessor的完整代碼如下

      @Component
      public class YamlConfigRefreshPostProcessor 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);
          }
      
          @Override
          public void setEnvironment(Environment environment) {
              this.environment = environment;
          }
      
          private void processMetaValue(Object bean) {
              Class clz = bean.getClass();
              if (!clz.isAnnotationPresent(RefreshValue.class)) {
                  return;
              }
      
              if (clz.isAnnotationPresent(ConfigurationProperties.class)) {
                  //@ConfigurationProperties注解
                  ConfigurationProperties config = (ConfigurationProperties) clz.getAnnotation(ConfigurationProperties.class);
                  for (Field field : clz.getDeclaredFields()) {
                      String key = config.prefix() + "." + field.getName();
                      if(mapper.containsKey(key)){
                          mapper.get(key).add(new FieldPair(bean, field, key));
                      }else{
                          List<FieldPair> fieldPairList = new ArrayList<>();
                          fieldPairList.add(new FieldPair(bean, field, key));
                          mapper.put(key, fieldPairList);
                      }
                  }
              } else {
                  //@Valuez注解
                  try {
                      for (Field field : clz.getDeclaredFields()) {
                          if (field.isAnnotationPresent(Value.class)) {
                              Value val = field.getAnnotation(Value.class);
                              String key = val.value().replace("${", "").replace("}", "");
                              if(mapper.containsKey(key)){
                                  mapper.get(key).add(new FieldPair(bean, field, key));
                              }else{
                                  List<FieldPair> fieldPairList = new ArrayList<>();
                                  fieldPairList.add(new FieldPair(bean, field, key));
                                  mapper.put(key, fieldPairList);
                              }
                          }
                      }
                  } catch (Exception e) {
                      e.printStackTrace();
                      System.exit(-1);
                  }
              }
          }
      
          public static class FieldPair {
              private static PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}",
                      ":", true);
              private Object bean;
              private Field field;
              private String value;
      
              public FieldPair(Object bean, Field field, String value) {
                  this.bean = bean;
                  this.field = field;
                  this.value = value;
              }
      
              public void updateValue(Environment environment) {
                  boolean access = field.isAccessible();
                  if (!access) {
                      field.setAccessible(true);
                  }
                  try {
                      if (field.getType() == String.class) {
                          String updateVal = environment.getProperty(value);
                          field.set(bean, updateVal);
                      }
                      else if (field.getType() == Integer.class) {
                          Integer updateVal = environment.getProperty(value,Integer.class);
                          field.set(bean, updateVal);
                      }
                      else if (field.getType() == int.class) {
                          int updateVal = environment.getProperty(value,int.class);
                          field.set(bean, updateVal);
                      }
                      else if (field.getType() == Boolean.class) {
                          Boolean updateVal = environment.getProperty(value,Boolean.class);
                          field.set(bean, updateVal);
                      }
                      else if (field.getType() == boolean.class) {
                          boolean updateVal = environment.getProperty(value,boolean.class);
                          field.set(bean, updateVal);
                      }
                      else {
                          String updateVal = environment.getProperty(value);
                          field.set(bean, JSONObject.parseObject(updateVal, field.getType()));
                      }
                  } catch (IllegalAccessException e) {
                      e.printStackTrace();
                  }
                  field.setAccessible(access);
              }
      
              public Object getBean() {
                  return bean;
              }
      
              public void setBean(Object bean) {
                  this.bean = bean;
              }
      
              public Field getField() {
                  return field;
              }
      
              public void setField(Field field) {
                  this.field = field;
              }
      
              public String getValue() {
                  return value;
              }
      
              public void setValue(String value) {
                  this.value = value;
              }
          }
      
          public static class ConfigUpdateEvent extends ApplicationEvent {
              String key;
      
              public ConfigUpdateEvent(Object source, String key) {
                  super(source);
                  this.key = key;
              }
          }
      
          @EventListener
          public void updateConfig(ConfigUpdateEvent configUpdateEvent) {
              if(mapper.containsKey(configUpdateEvent.key)){
                  List<FieldPair> fieldPairList = mapper.get(configUpdateEvent.key);
                  if(fieldPairList.size()>0){
                      for (FieldPair fieldPair:fieldPairList) {
                          fieldPair.updateValue(environment);
                      }
                  }
              }
          }
      }
      
      
      posted @ 2022-12-31 19:29  code2roc  閱讀(833)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲一区在线成人av| 中文字幕av无码不卡| 91区国产福利在线观看午夜| 富民县| 免费观看成人毛片a片| 韩国福利视频一区二区三区| 亚洲情综合五月天| 日韩在线观看精品亚洲| 深夜免费av在线观看| 东京热人妻丝袜无码AV一二三区观 | 成人精品区| 最新中文乱码字字幕在线| av天堂午夜精品一区| 国产精品爽爽久久久久久竹菊| 中文字幕乱码熟妇五十中出| 少妇午夜啪爽嗷嗷叫视频| 精品无码专区久久久水蜜桃| 成人午夜视频一区二区无码| 国产精品中出一区二区三区| 欧美精品亚洲精品日韩专| 蜜臀av久久国产午夜福利软件| 日韩精品一卡二卡三卡在线| 狠狠色狠狠色综合日日不卡| 国产一区二区视频在线看| 116美女极品a级毛片| 十八岁污网站在线观看| 亚洲精品尤物av在线网站| 国产激情一区二区三区午夜| 成人精品视频一区二区三区| 国产99在线 | 免费| 亚洲区日韩精品中文字幕| 中文字幕精品人妻av在线| 四虎库影成人在线播放| 亚洲天堂av免费在线看| 国产999久久高清免费观看| 国产精品成人中文字幕| 日本亚洲一区二区精品久久| 好男人好资源WWW社区| 无码h黄肉动漫在线观看| 夜夜影院未满十八勿进| 亚洲av无码之国产精品网址蜜芽|