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

Posted FeeCy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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----------------------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

以上是关于SpringBoot 动态配置数据源-进阶-可视化动态配置数据源-2的主要内容,如果未能解决你的问题,请参考以下文章

使用Springboot+MybatisPlus+Echarts+Mysql从0-1完成中国疫情地图可视化动态展示

一套基于SpringBoot+Vue+Shiro 前后端分离 开发的代码生成器

SpringBoot 快速整合Mybatis(去XML化+注解进阶)

springboot怎样动态配置数据库并设置默认数据源?

SpringBoot进阶之访问数据库(含源码)

SpringBoot进阶