採用spring zookeeper 實現簡單的配置管理

中午君發表於2019-01-23
  • 摘要:實現思路利用Spring中BeanFactoryPostProcessor的機制,在Spring載入完配置檔案完成Bean定義之後,通過讀取zk中配置內容,覆蓋BeanDefiniton中的propertyvalues實現過程配置相關注解定義@Config註解用於說明該類為配置類用於儲存相關配置資訊@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceC
  • 實現思路

    利用Spring中BeanFactoryPostProcessor的機制,在Spring載入完配置檔案完成Bean 定義之後,通過讀取zk中配置內容,覆蓋Bean Definiton中的property values

    實現過程
    配置相關注解定義

    @Config 註解用於說明該類為配置類用於儲存相關配置資訊


    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    public @interface Config { 
    } 複製程式碼

    @ConfigField 註解用於標註類的屬性欄位對應配置中的配置項

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented 
    public @interface ConfigField { 
        String key(); 
    } 複製程式碼
    自定義BeanFactoryPostProcessor, 此處實現了介面beanDefinitionRegistryPostProcessor

    該類主要用於實現方法 postProcessBeanDefinitionRegistry;其核心程式碼如下

    // 獲取spring解析後的Bean 定義資訊 
    for (String beanName : beanDefinitionRegistry.getBeanDefinitionNames()) { 
         BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanName); 
         String className = beanDefinition.getBeanClassName(); 
         Class<?> clazz = Class.forName(className); 
         // 判斷該類是否為配置類 
         if (isConfigBean(clazz)) { 
               // path 為 zk中應用對應節點 /config/app/env 
               String configPath = path + "/" + className; 
               Stat stat = curatorFramework.checkExists().forPath(configPath); 
               if (stat != null) { 
                    String value = new String(curatorFramework.getData().forPath(configPath)); 
                    JSONObject jsonObject = JSONObject.parseObject(value); 
                    // 獲取配置類中欄位與配置項key的對應 
                    Map configFieldMap = findConfigFieldAnnation(clazz); 
                    for (String key : jsonObject.keySet()) { 
                           String propertyValue = jsonObject.getString(key); 
                           String property = configFieldMap.get(key); 
                           // 採用zk中的值覆蓋到 Bean Definiton propertyValue 
                           beanDefinition.getPropertyValues().add(property, propertyValue); 
                    }      
              log.info("Hxlzp config register bean : {}, property values : {}", className, value); 
              } 
        } 
    } 複製程式碼

    判斷類是否為配置類

    private boolean isConfigBean(Class<?> clazz) { 
             return clazz.getAnnotation(Config.class) != null; 
    } 複製程式碼

    獲取配置類的屬性與配置項的對應關係

    private Map findConfigFieldAnnation(Class<?> clazz) { 
       Map map = new HashMap (); 
      for (Field field : clazz.getDeclaredFields()) { 
           ConfigField configField = field.getAnnotation(ConfigField.class); 
           if (configField != null) { 
              map.put(configField.key(), field.getName()); 
            } 
      } 
      return map; 
    }複製程式碼
    zk 節點定義

    如下圖所示:二級為應用節點, 三級為應用的所屬環境節點,四級為應用的各個環境下的配置節點,配置節點約束節點名必須與配置類的類名保持一致


    採用spring zookeeper 實現簡單的配置管理_Java


    zk 配置節點的資料格式

    配置節點資料採用json格式;如下


    {
    "db.username" : "",
    "db.password" : "",
    "db.url" : ""
    }
    使用示例
    定義DB配置類

    定義類 DbConfig, 用於記錄Db相關配置



    @Data 
    @Config 
    @Component("dbConfig") 
    public class DbConfig { 
    @ConfigField(key = "db.url") 
    private String url; 
    @ConfigField(key = "db.username") 
    private String username; 
    @ConfigField(key = "db.password") 
    private String password; 
    } 複製程式碼
    定義DataSource

    此處採用 Druid作為資料來源


    @Slf4j 
    @Data 
    public class ExampleDatasource extends DruidDataSource { 
    @Resource 
    private DbConfig dbConfig; 
    /** 
    * 重寫init;實現db配置後初始化 
    */ 
    public void init () throws SQLException { 
    if (!this.dbConfig.getUrl().equals(this.getUrl()) || 
    !this.dbConfig.getUsername().equals(this.getUsername()) || !this.dbConfig.getPassword().equals(this.getPassword())) { 
    // 說明首次初始化 或 db配置發生變化 此處也即實現了在無需重啟的情況下完成db資料來源的動態切換 
    this.inited = false; 
    } 
    if (inited) { 
    // 因 getConnection 的時候 再次呼叫了init 需判斷是否已初始化過 
    return; 
    } 
    if (dbConfig != null) { 
    log.info("datasource set db config , {}", dbConfig.getUrl()); 
    this.setUrl(dbConfig.getUrl()); 
    this.setUsername(dbConfig.getUsername()); 
    this.setPassword(dbConfig.getPassword()); 
    super.init(); 
    } 
    } 
    } 複製程式碼
    Spring 配置

    配置檔案中需定義 上文中 BeanDefinitionRegistryPostProcessor; 示例如下


    init-method="init" destroy-method="close">

    測試

    可以通過從ApplicationContext 中獲取dao執行簡單的操作驗證; 也可以修改zk中節點配置資料 驗證資料來源的動態切換


    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); 
    classPathXmlApplicationContext.start(); 
    classPathXmlApplicationContext.getBean(UserDao.class).getById(1); 複製程式碼
  • 以上是採用spring zookeeper 實現簡單的配置管理的內容,更多 Zookeeper 採用 配置 簡單 實現 spring 管理 的內容,請您使用右上方搜尋功能獲取相關資訊。


相關文章