zoukankan      html  css  js  c++  java
  • 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----------------------------------

      

  • 相关阅读:
    composer "Illegal offset type in isset or empty"报错解决方案
    Yii2 使用a标签发送post请求
    YII2 项目安装步骤及异常记录
    三级城市联动菜单
    最新全国省、市、县数据库
    Sanic官翻-SSL示例
    Sanic官翻-WebSocket
    Sanic官翻-部署
    Sanic官翻-概述
    老子的道德经
  • 原文地址:https://www.cnblogs.com/feecy/p/12108702.html
Copyright © 2011-2022 走看看