文档章节

采用spring zookeeper 实现简单的配置管理

hxlzpnyist
 hxlzpnyist
发布于 2017/09/01 19:05
字数 807
阅读 142
收藏 0

实现思路

利用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<String, String> 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<String, String> findConfigFieldAnnation(Class<?> clazz) {
    Map<String, String> map = new HashMap<String, String>();

    for (Field field : clazz.getDeclaredFields()) {
        ConfigField configField = field.getAnnotation(ConfigField.class);

        if (configField != null) {
            map.put(configField.key(), field.getName());
        }
    }

    return map;
}
zk 节点定义

如下图所示:二级为应用节点, 三级为应用的所属环境节点,四级为应用的各个环境下的配置节点,配置节点约束节点名必须与配置类的类名保持一致

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; 示例如下

<!-- 扫描目录 -->
<context:component-scan base-package="com.xxxx.xxx.xxx" />
<!-- hxlzpConfig 即为自定义的 BeanDefinitionRegistryPostProcessor 实现类-->
<bean id="hxlzpConfig" class="com.xxx.xxx.xxx.hxlzpConfig" destroy-method="destroy" />
<!-- 数据源 -->
<bean id="newDataSource" class="com.bj58.hxlzp.config.example.ExampleDatasource" scope="singleton"
      init-method="init" destroy-method="close">
    <!-- 配置初始化大小、最小、最大 -->
    <property name="initialSize" value="1" />
    <property name="minIdle" value="1" />
    <property name="maxActive" value="200" />
    <property name="...." value="...."/>
</bean>
<!-- mapper文件扫描包 -->
<bean id="newSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"></bean>
<!-- mapper接口扫描包 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"></bean>
测试

可以通过从ApplicationContext 中获取dao执行简单的操作验证; 也可以修改zk中节点配置数据 验证数据源的动态切换

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        classPathXmlApplicationContext.start();

classPathXmlApplicationContext.getBean(UserDao.class).getById(1);

© 著作权归作者所有

hxlzpnyist
粉丝 4
博文 27
码字总数 9099
作品 0
信阳
私信 提问
Spring Cloud简介/版本选择/ZooKeeper例子搭建简单说明

一、什么是Spring Cloud 官方的说法就是Spring Cloud 给开发者提供一套按照一定套路快速开发分布式系统的工具。 具体点就是Spring Boot实现的微服务架构开发工具。它为微服务架构中涉及的配置...

easonjim
2017/09/18
0
0
Thrift RPC实战(七) 基于zookeeper和thrift的RPC服务发布订阅

对于Thrift服务化的改造,主要是客户端,可以从如下几个方面进行: 1.服务端的服务注册,客户端自动发现,无需手工修改配置,这里我们使用zookeeper,但由于zookeeper本身提供的客户端使用较...

lemonLove
2018/06/26
0
0
基于开源Dubbo分布式RPC服务框架的部署整合

详细介绍请参照官方地址:http://alibaba.github.io/dubbo-doc-static/Home-zh.htm,不再重复描述,本文主要记录了详细的开发整合步骤。 一、源码构建 这个网上很多教程,大家可以google/ba...

hanfeng
2015/11/03
0
1
java B2B2C源码电子商城系统-Kafka快速入门

大家对Kafka有了一些基本了解之后,下面我们来尝试构建一个Kafka服务端,并体验一下基于Kafka的消息生产与消费。 需要JAVA Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码 ...

明理萝
03/18
0
0
dubbo + zookeeper的相关应用

一、首先介绍下DUBBO的背景 随着互联网的发展,一些大型网站的规模不断扩大,常规的垂直应用架构已经无法满足,分布式架构已经势在必行,DUBBO是一个分布式服务框架,在这种情况下诞生的。 ...

zheng854938169
2018/06/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

基础工具类

package com.atguigu.util;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;import javax.sql.DataSource;import com.alibaba.druid......

architect刘源源
今天
43
0
P30 Pro劲敌!DxO官宣新机:排行榜又要变

5月26日晚间,DxOMark官方推特预告,将在5月27日公布一款新机型的DxOMark评分,猜猜是哪款? 网友猜想的机型有:红米K20、谷歌Pixel 3a、索尼Xperia 1、诺基亚9 PureView等。 DxOMark即将公布...

linux-tao
昨天
15
0
Ubuntu18.04.2窗口过小不能自适应(二次转载)

解决Ubuntu在虚拟机窗口不能自适应 2018年09月06日 16:20:08 起不了名儿 阅读数 855 此博文转载:https://blog.csdn.net/nuddlle/article/details/77994080(原地址) 试了很多办法这个好用 ...

tahiti_aa
昨天
2
0
死磕 java同步系列之CountDownLatch源码解析

问题 (1)CountDownLatch是什么? (2)CountDownLatch具有哪些特性? (3)CountDownLatch通常运用在什么场景中? (4)CountDownLatch的初始次数是否可以调整? 简介 CountDownLatch,可以...

彤哥读源码
昨天
6
0
Nginx提供下载apk服务

有时候我们可能需要提供文件或者其他apk下载链接,通过 nginx 配置可以很简单地实现。 server {    listen 80;    server_name download.xxx.com;    root app;    locati...

Jack088
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部