SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-2

接 SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-1

配置文件修改后,需要使配置生效

采用springcloud 配置 jar :  spring-cloud-starter-config + spring-boot-starter-actuator

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

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-config</artifactId>
	<version>2.0.5.RELEASE</version>
</dependency>

application.yml 添加配置

management:
  endpoints:
    web:
      exposure:
        include: refresh

配置参数刷新

@Component
@Getter
@RefreshScope
public class ApplicationConfig {
    @Value("${XXX.XXX}")
     private String XXX;
    @Autowired
    private Environment environment;
}
动态数据源- AbstractRoutingDataSource(每执行一次数据库,动态获取DataSource)
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }

    /**
     * 动态更新自定义数据源
     * @param defaultDataSource
     * @param customDataSources
     */
    public void updateTargetDataSource(DataSource defaultDataSource,Map<String,DataSource> slaveDataSources){
        Map<Object,Object> customDS=new HashMap<Object, Object>();
        customDS.putAll(slaveDataSources);
        setTargetDataSources(customDS);
        setDefaultTargetDataSource(defaultDataSource);
        afterPropertiesSet();
    }
}
动态数据源通知-DynamicDataSourceAspect 
@Aspect
@Order(-1)//保证在@Transactional之前执行
@Component
public class DynamicDataSourceAspect {
    //改变数据源
    public void changeDataSource( String  targetDataSource) {
        if (StringUtil.isNotEmpty(targetDataSource) &&
                DynamicDataSourceContextHolder.isContainsDataSource(targetDataSource)) {
            DynamicDataSourceContextHolder.setDataSourceType(targetDataSource);
        }
    }

    public void clearDataSource( String  targetDataSource) {
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

 动态数据源上下文管理 -DynamicDataSourceContextHolder

public class DynamicDataSourceContextHolder {
    //存放当前线程使用的数据源类型信息
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    //存放数据源id
    public static Set<String> dataSourceIds = new HashSet<>();

    //设置数据源
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    //获取数据源
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    //清除数据源
    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    //判断当前数据源是否存在
    public static boolean isContainsDataSource(String dataSourceId) {
        return dataSourceIds.contains(dataSourceId);
    }
}

 注册动态数据源-初始化数据源和提供了执行动态切换数据源的工具类- EnvironmentAware(获取配置文件配置的属性值)

@Slf4j
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    @Override
    public void setEnvironment(Environment environment) {
        DynamicDataSourceRegisterUtil.initDefaultDataSource(environment);
        DynamicDataSourceRegisterUtil.initSlaveDataSources(environment);
    }

    /**
     * 初始化注册数据源 BeanDefinition
     * @param annotationMetadata
     * @param beanDefinitionRegistry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        //添加默认数据源
        targetDataSources.put(DynamicDataSourceRegisterUtil.DEFAULT_TARGET_DATA_SOURCE, DynamicDataSourceRegisterUtil.defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add(DynamicDataSourceRegisterUtil.DEFAULT_TARGET_DATA_SOURCE);
        //添加其他数据源
        targetDataSources.putAll(DynamicDataSourceRegisterUtil.slaveDataSources);
        DynamicDataSourceContextHolder.dataSourceIds.addAll(DynamicDataSourceRegisterUtil.slaveDataSources.keySet());
        //创建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue(DynamicDataSourceRegisterUtil.DEFAULT_TARGET_DATA_SOURCE, DynamicDataSourceRegisterUtil.defaultDataSource);
        mpv.addPropertyValue(DynamicDataSourceRegisterUtil.TARGET_DATA_SOURCES, targetDataSources);
        //注册 - BeanDefinitionRegistry
        beanDefinitionRegistry.registerBeanDefinition(
                DynamicDataSourceRegisterUtil.DATA_SOURCE, beanDefinition);
        log.info("Dynamic DataSource Registry");
    }
}

 数据源注册工具类-DynamicDataSourceRegisterUtil  配合DynamicDataSourceRegister

@Slf4j
public class DynamicDataSourceRegisterUtil {
    private static final ConversionService conversionService = new DefaultConversionService();
    public final static  String DEFAULT_TARGET_DATA_SOURCE ="defaultTargetDataSource";
    //默认数据源
    public static DataSource defaultDataSource;
    //用户自定义数据源
    public static Map<String, DataSource> slaveDataSources = new HashMap<>();
    public final static  String TARGET_DATA_SOURCES  ="targetDataSources";
    public final static  String DATA_SOURCE ="dataSource";
    /**
     * 创建DataSource
     * @param dataSourceMap
     * @return
     */
    public static DataSource buildDataSource(Map<String, Object> dataSourceMap) {
        DataSource dataSource=null;
        try {
            dataSource= DruidDataSourceFactory.createDataSource(dataSourceMap);
            if(dataSource instanceof DruidDataSource){
                //注意:这一设置是为解决Druid 在获取连接时由于连接配置出错会一直等待获取连接,比较重要
                ((DruidDataSource) dataSource).setBreakAfterAcquireFailure(true);
            }
            return dataSource;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 初始化主数据源
     */
    public static void initDefaultDataSource(Environment env) {
        // 读取主数据源
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("driver", env.getProperty("spring.datasource.driver-class-name"));
        dsMap.put("url", env.getProperty("spring.datasource.url"));
        dsMap.put("username", env.getProperty("spring.datasource.username"));
        dsMap.put("password", env.getProperty("spring.datasource.password"));
        defaultDataSource = buildDataSource(dsMap);
    }
    /**
     * 关闭老的数据源
     */
    private static void closeOldCustomDataSources(){
        if(slaveDataSources!=null&&slaveDataSources.size()>0){
            for (String key:slaveDataSources.keySet()){
                DataSource dataSource =slaveDataSources.get(key);
                if(dataSource instanceof DruidDataSource){
                    ((DruidDataSource)dataSource).close();
                    log.info("closed datasource "+key);
                }
            }
        }
        if(slaveDataSources!=null){
            slaveDataSources.clear();
        }
    }

    /**
     * 初始化更多数据源
     *
     */
    public static void initSlaveDataSources(Environment env) {
        //初始化之前要先将老的数据源关闭
        closeOldCustomDataSources();
        // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
        String dsPrefixs = env.getProperty("slave.datasource.names");
        for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
            Map<String, Object> dsMap = new HashMap<>();
            dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver-class-name"));
            dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url"));
            dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username"));
            dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password"));
            DataSource dataSource= buildDataSource(dsMap);
            slaveDataSources.put(dsPrefix, dataSource);
        }
    }
    /**
     * 更新配置文件,并热加载到Environment中
     */
    public synchronized static void refreshDataSoureProperties(Environment environment) {
        //将属性持久化到配置文件
        refreshDataSource(environment);
    }
    /**
     * 更新配置之后要更新DynamicDataSource
     * @param environment
     */
    private static void refreshDataSource(Environment environment) {
        initSlaveDataSources(environment);
        DynamicDataSource dynamicDataSource =ApplicationContextUtil.getBean(DATA_SOURCE);
        dynamicDataSource.updateTargetDataSource(defaultDataSource,slaveDataSources);
        DynamicDataSourceContextHolder.dataSourceIds.clear();
        DynamicDataSourceContextHolder.dataSourceIds.add(DEFAULT_TARGET_DATA_SOURCE);
        DynamicDataSourceContextHolder.dataSourceIds.addAll(slaveDataSources.keySet());
    }
}

  springboot 启动Application 增加注解引入

@Import({DynamicDataSourceRegister.class})

  

调用:

  页面调用接口修改Yaml文件配置项后

  

//将配置增加到上下文环境中-spring-cloud 包含的接口
POST:
    http://127.0.0.1:8080/XXX/actuator/refresh
//返回后,调用自己的refresh-API 将数据源加载
    @PostMapping("/refresh")
    public JSONObject refresh(){
         //数据源更新      DynamicDataSourceRegisterUtil.refreshDataSoureProperties(configCommon.getEnvironment());
     //TODO 其他业务代码
        JSONObject result = new JSONObject();
        result.put("code",200);
        result.put("msg","初始化配置成功");
        return result;
    }

  也可以自己封装一下 Java-http调用 /actuator/refresh,再更新数据源配置

完结

-----------------------20191230----------------------------------

  

原文地址:https://www.cnblogs.com/feecy/p/12108702.html

时间: 2024-10-12 21:26:15

SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-2的相关文章

SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-1

固定配置多数据源:https://www.cnblogs.com/feecy/protected/p/11847207.html springboot-yml 配置编辑 节点配置: slave: datasource: names: N1,N2 N1: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://XXXXXXXXXXX1 username: root password: type: com.alibaba.druid.po

搞定SpringBoot多数据源(2):动态数据源

目录 1. 引言 2. 动态数据源流程说明 3. 实现动态数据源 3.1 说明及数据源配置 3.1.1 包结构说明 3.1.2 数据库连接信息配置 3.1.3 数据源配置 3.2 动态数据源设置 3.2.1 动态数据源配置 3.2.2 动态选择数据源 3.2.3 动态数据源使用 3.3 使用 AOP 选择数据源 3.3.1 定义数据源注解 3.3.2 定义数据源切面 3.3.3 使用 AOP 进行数据源切换 4. 再思考一下 5. 总结 参考资料 往期文章 一句话概括:使用动态数据源对多个数据库

动态路由协议RIP的基本原理与配置

  前面学习了静态路由的原理,但是用静态路由搭建网络时,每一条路由的变更都需要手动进行配置,这在大型网络中的工作量是非常巨大的.有没有办法解决呢?答案是肯定的,使用动态路由即可解决此问题 动态路由协议基础 1. 动态路由概述 动态路由是网络中路由器之间互相通信,传递路由信息,利用收到的路由信息更新路由表的过程.它能实时的适应网络结构的变化.如果路由更新信息表明网络发生了变化,路由选择软件就会重新计算路由,并发出新的路由更新信息.这些信息通过各个网络,引起各路由器重新启动其路由算法,并更新各自的路

tomato路由器上配置花生壳DDNS动态域名

Google最近被封的很彻底,之前在Google SVN服务器上托管的代码不好访问了. 算是废物利用吧, 我把老笔记本跑的慢死的Win8卸了,装了Ubuntu,架了SVN服务器,把一些个人代码都放在上面管理.在家里和公司进行提交同步. 上海电信的网络是有公网IP的,我N年前还在山东淄博的时候注册的花生壳DDNS帐号竟然还能用,当时免费的4个二级域名也是OK的.于是就用花生壳搞定DDNS,用域名来访问SVN服务. 在工作笔记本上跑花生壳客户端还是麻烦,这个笔记本又不能一直开着,所以访问起来就有一些

配置静态路由和动态路由详解

配置路由信息的两种方式简单介绍: 因为路由器的默认路由表只有直连的路由信息,所以需要配置不是直连的路由信息. 配置路由,不明思义就是告诉路由器不知道的路由. 如图设置路由,目的为使R1能ping通R3,R3能ping通R1. 静态路由配置:ip address 目标网段号 子网掩码 下一跳ip R1: en conf t Ip address  192.168.2.0  255.255.255.0  192.168.1.2 R3: en conf t Ip address  192.168.1.

ES 12 - 配置使用Elasticsearch的动态映射(dynamic mapping)

目录 1 动态映射(dynamic mapping) 1.1 什么是动态映射 1.2 体验动态映射 1.3 搜索结果不一致的原因分析 2 开启dynamic mapping策略 2.1 约束策略 2.2 策略示例 3 定制dynamic mapping策略 3.1 date_detection - 日期识别策略 3.2 在type中自定义动态映射模板 3.3 [过期]在index中自定义默认映射模板 1 动态映射(dynamic mapping) 1.1 什么是动态映射 动态映射时Elastic

Spring实现动态数据源,支持动态加入、删除和设置权重及读写分离

当项目慢慢变大,訪问量也慢慢变大的时候.就难免的要使用多个数据源和设置读写分离了. 在开题之前先说明下,由于项目多是使用Spring,因此下面说到某些操作可能会依赖于Spring. 在我经历过的项目中,见过比較多的读写分离处理方式,主要分为两步: 1.对于开发者,要求serivce类的方法名必须遵守规范,读操作以query.get等开头,写操作以update.delete开头. 2.配置一个拦截器,根据方法名推断是读操作还是写操作,设置对应的数据源. 以上做法能实现最简单的读写分离.但对应的也会

Spring实现动态数据源,支持动态添加、删除和设置权重及读写分离

当项目慢慢变大,访问量也慢慢变大的时候,就难免的要使用多个数据源和设置读写分离了. 在开题之前先说明下,因为项目多是使用Spring,因此以下说到某些操作可能会依赖于Spring. 在我经历过的项目中,见过比较多的读写分离处理方式,主要分为两步: 1.对于开发人员,要求serivce类的方法名必须遵守规范,读操作以query.get等开头,写操作以update.delete开头. 2.配置一个拦截器,依据方法名判断是读操作还是写操作,设置相应的数据源. 以上做法能实现最简单的读写分离,但相应的也

SpringBoot学习笔记(1):配置Mybatis

SpringBoot学习笔记(1):配置Mybatis 参考资料: 1.AndyLizh的博客 2.xiaolyuh123的博客 快速开始 添加Mybatis依赖(其他依赖已省去) <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId