与XML和Annotation两种配置方式不一样点在于: 前两种方式XML和Annotation的配置方式为预约义方式,即开发人员经过XML文件或者Annotation预约义配置Bean的各类属性后,启动Spring容器,Spring容器会首先解析这些配置属性,生成对应的Bean Definition,装入到DefaultListtableBeanFactory对象的属性容器中,以此同时,Spring框架也会定义内部使用的Bean定义,如Bean名为:org.springframework.context.annotation.internalConfigurationAnnotationProcessor”的 ConfigurationClassPostProcessor 定义。然后此刻不会作任何Bean Definition的解析动做,Spring框架会根据前两种配置,过滤出BeanDefinitionRegistryPostProcessor 类型的Bean定义,并经过Spring框架生成对应的Bean对象(如 ConfigurationClassPostProcessor 实例)。。结合 Spring 上下文源码可知这个对象是一个 processor 类型工具类,Spring 容器会在实例化开发人员所定义的 Bean 前先调用该 processor 的 postProcessBeanDefinitionRegistry(…) 方法。此处实现基于 Java Code 配置Bean Definition的处理。 基于 Java Code 的配置方式,其执行原理不一样于前两种。它是在 Spring 框架已经解析了基于 XML 和 Annotation 配置后,经过加入 BeanDefinitionRegistryPostProcessor 类型的 processor 来处理配置信息,让开发人员经过 Java 编程方式定义一个 Java 对象。其优势在于能够将配置信息集中在必定数量的 Java 对象中,同时经过 Java 编程方式,比基于 Annotation 方式具备更高的灵活性。而且该配置方式给开发人员提供了一种很是好的范例来增长用户自定义的解析工具类。其主要缺点在于与 Java 代码结合紧密,配置信息的改变须要从新编译 Java 代码,另外这是一种新引入的解析方式,须要必定的学习成本。
org.springframework.context.ApplicationContextAware 它的setApplicationContext 方法将在Spring启动以前第一个被调用。咱们用来同时启动Jdon框架。css
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor 它的postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 方法是第二和第三被调用,它们在Bean初始化建立以前启动,若是Spring的bean须要的其余第三方中的组件,咱们在这里将其注入给Spring。java
org.springframework.context.EnvironmentAware 这里介绍凡是被spring管理的类,实现接口 EnvironmentAware 重写方法 setEnvironment 能够在工程启动时,获取到系统环境变量和application配置文件中的变量。mysql
@ConditionOnClass代表该@Configuration仅仅在必定条件下才会被加载,这里的条件是 DataDruidConfig.class位于类路径上web
@EnableConfigurationProperties将Spring Boot的配置文件(application.properties)中的spring.datasource.druid.*属性映射为DataDruidProperties并注入到 DataDruidConfig中。spring
@ConditionalOnMissingBean说明Spring Boot仅仅在当前上下文中不存在DataDruidConfig对象时,才会实例化一个Bean。这个逻辑也体现了Spring Boot的另一个特性——自定义的Bean优先于框架的默认配置,咱们若是显式的在业务代码中定义了一个DataDruidConfig对象,那么Spring Boot就再也不建立。sql
package top.suven.core.db; /** * DruidDataSource 数据库链接管理对象的相关参数属性配置对象; * 数据库链接池的相结合配置文件类型定义参数; */ public interface DataDruidConfig { /** * DruidDataSource 对应属性的配置文件参数;可对应变动和修改 * 根据本身的项目配置文件规则,只须要调整***PREFIX对应的参数便可; */ String DATASOURCE_DRUID_PREFIX = "suven.datasource.druid."; /** * 数据库对象的模块的url,username,password的前缀 * 根据本身的项目配置文件规则,只须要调整***PREFIX对应的参数便可; * suven.datasource.%s.%s.url //eg: suven.datasource.assets.master.url * suven.datasource.%s.%s.username //eg: suven.datasource.assets.master.username * suven.datasource.%s.%s.password; //eg: suven.datasource.assets.master.password */ String DATASOURCE_MODULE_PREFIX = "suven.datasource."; String DATASOURCDE_DRUID_FORMAT = "%s.%s."; /** * 经过 BeanDefinitionBuilder类,初始化DruidDataSource对应的属性参考 */ String URL = "url"; String USERNAME = "username"; String PASSWORD = "password"; String DRIVER_CLASSNAME = "driverClassName"; String INITIALIZE = "initialize"; String DBTYPE = "dbType"; String MAXACTIVE = "maxActive"; String INITIALSIZE = "initialSize"; String MAXWAIT = "maxWait"; String MINIDLE = "minIdle"; String TIME_BETWEENE_VICTION_RUNS_MILLIS = "timeBetweenEvictionRunsMillis"; String MIN_EVICTABLE_IDLE_TIME_MILLIS = "minEvictableIdleTimeMillis"; String VALIDATION_QUERY = "validationQuery"; String TEST_WHILEIDLE = "testWhileIdle"; String TEST_ON_BORROW = "testOnBorrow"; String TEST_ON_RETURN = "testOnReturn"; String POOL_PREPARED_STATEMENTS = "poolPreparedStatements"; String CONNECTION_PROPERTIES = "connectionProperties"; String FILTERS = "filters"; String ENABLED = "enabled"; String datasource_druid_master = "master"; String datasource_druid_slave ="slave"; String datasource_druid_frame ="druid"; String datasource_master_name = "MasterDataSource"; String datasource_slave_name = "SlaveDataSource"; String datasource_param_config_enabled = "config.enabled"; /** * 初化全部数据源DataSourceAutoConfiguration类配置开关,默认为falase * suven.datasource.druid.frame.enabled=true */ String datasource_druid_config_enabled = DATASOURCE_DRUID_PREFIX + datasource_param_config_enabled ;//"suven.datasource.druid.frame.enabled"; /** * 初化指定模块数据源DataSourceGroupNameEnum类配置开关,默认为 true * suven.datasource.user.master.enabled=true */ String datasource_druid_master_enabled = DATASOURCE_MODULE_PREFIX + DATASOURCDE_DRUID_FORMAT + ENABLED;// "suven.datasource.%s.%s.enabled"; /** * 初化全部数据源DataSourceAutoConfiguration类配置 从数据库的总开关, * 对应数据库的从数据库集合开关;默认为 true, * 若存在对应的从数据库的配置会自动加载,若须要关闭能够将该配置设计为false,或删除对应配置 * suven.datasource.druid.user.slave.enabled=true */ String datasource_druid_slave_enabled = DATASOURCE_MODULE_PREFIX + DATASOURCDE_DRUID_FORMAT + ENABLED;// "suven.datasource.druid.%s.slave.enabled"; String datasource_druid_url = DATASOURCE_MODULE_PREFIX + DATASOURCDE_DRUID_FORMAT + URL;// "suven.datasource.%s.%s.url";// = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 String datasource_druid_username = DATASOURCE_MODULE_PREFIX + DATASOURCDE_DRUID_FORMAT + USERNAME;// "suven.datasource.%s.%s.username";//suven.datasource.assets.master.username = redfinger String datasource_druid_password = DATASOURCE_MODULE_PREFIX + DATASOURCDE_DRUID_FORMAT + PASSWORD;// "suven.datasource.%s.%s.password";// suven.datasource.assets.master.password = redfinger String datasource_druid_initialSize = DATASOURCE_DRUID_PREFIX + INITIALSIZE;// "suven.datasource.druid.initialSize"; String datasource_druid_minIdle = DATASOURCE_DRUID_PREFIX + MINIDLE;//"suven.datasource.druid.minIdle"; String datasource_druid_maxActive = DATASOURCE_DRUID_PREFIX + MAXACTIVE;// "suven.datasource.druid.maxActive"; String datasource_druid_maxWait = DATASOURCE_DRUID_PREFIX + MAXWAIT;// "suven.datasource.druid.maxWait"; String datasource_druid_timeBetweenEvictionRunsMillis = DATASOURCE_DRUID_PREFIX + TIME_BETWEENE_VICTION_RUNS_MILLIS;// "suven.datasource.druid.timeBetweenEvictionRunsMillis"; String datasource_druid_minEvictableIdleTimeMillis = DATASOURCE_DRUID_PREFIX + MIN_EVICTABLE_IDLE_TIME_MILLIS;// "suven.datasource.druid.minEvictableIdleTimeMillis"; String datasource_druid_validationQuery = DATASOURCE_DRUID_PREFIX + VALIDATION_QUERY;// "suven.datasource.druid.validationQuery"; String datasource_druid_testWhileIdle = DATASOURCE_DRUID_PREFIX + TEST_WHILEIDLE;// "suven.datasource.druid.testWhileIdle"; String datasource_druid_testOnBorrow = DATASOURCE_DRUID_PREFIX + TEST_ON_BORROW;// "suven.datasource.druid.testOnBorrow"; String datasource_druid_testOnReturn = DATASOURCE_DRUID_PREFIX + TEST_ON_RETURN;// "suven.datasource.druid.testOnReturn"; String datasource_druid_poolPreparedStatements = DATASOURCE_DRUID_PREFIX + POOL_PREPARED_STATEMENTS;// "suven.datasource.druid.poolPreparedStatements"; String datasource_druid_filters = DATASOURCE_DRUID_PREFIX + FILTERS;// "suven.datasource.druid.filters"; String datasource_druid_connectionProperties = DATASOURCE_DRUID_PREFIX + CONNECTION_PROPERTIES;// "suven.datasource.druid.connectionProperties"; }
package top.suven.core.db; import com.alibaba.druid.pool.DruidDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * 获取数据源 * * @author suven * @version 2018/6/15 13:18 */ /** * 将 DataSourceGroupNameEnum 数据源组 生成bean对象注入到spring管理容器中; * 经过 datasource_druid_enabled 对应模块数据库配置总开关,默认值为true * 经过 datasource_druid_slave_enabled 对应模块从数据库配置总开关,默认值为true * 将模块初始化后数据源信息,并将结果生成spring bean 名称缓存到DataSourceGroupNameEnum对象中 * 将全部模块经过initDataByGroup 初始化后的数据库的聚群,初始化到目标的动态数据池子里; * 再根据模块名称字符串 做为模块key 从targetDataSources 的Map中获取指定模块的数据库的链接池; * 从而经过动态桥接的设计模式来达到数据源链接池动态切换原理; */ public class DynamicDataSource extends AbstractRoutingDataSource { private final Logger logger = LoggerFactory.getLogger(getClass()); private boolean isDefaultTargetDataSource = true; private Map<Object, Object> targetDataSources = new HashMap<>(); private ConcurrentHashMap<String,AtomicInteger> currentMap = new ConcurrentHashMap<>(); /** * 将全部模块经过initDataByGroup 初始化后的数据库的聚群,初始化到目标的动态数据池子里; */ public void setTargetDataSources(){ super.setTargetDataSources(targetDataSources); } public void setTargetDataSources(Map<Object, Object> targetDataSources){ Object defaultTargetDataSource = targetDataSources.entrySet().iterator().next(); if(isDefaultTargetDataSource){ this.setDefaultTargetDataSource(defaultTargetDataSource); isDefaultTargetDataSource =false; } this.targetDataSources.putAll(targetDataSources); this.setTargetDataSources(); } /** * DataSourceGroupNameEnum 枚举类型的实现初始化指定模块数据源信息; * @param dataSourceGroupNameEnum */ public void initDataByGroup(DataSourceGroupNameEnum dataSourceGroupNameEnum,ApplicationContext applicationContext){ Map<Object, Object> targetDataSources = new HashMap<>(); DataSourceGroup group = DataSourceGroupNameEnum.getDataSourceGroupByName(dataSourceGroupNameEnum); if(null == group || null == group.getMasterSources()){ return; } String master = group.getMasterSources(); DataSource defaultTargetDataSource = applicationContext.getBean(master,DruidDataSource.class); targetDataSources.put(master, defaultTargetDataSource); List<String> list = group.getSlaveSources(); if(null != list && !list.isEmpty()){ for (String slave : list){ try{ DataSource datasource = applicationContext.getBean(slave,DruidDataSource.class); if(null == datasource){ continue; } targetDataSources.put(slave,datasource ); }catch (Exception e){ logger.info("初始化[{}】 slave 数据源失败,检查slave开关是否开启!", slave); } } } if(isDefaultTargetDataSource){ this.setDefaultTargetDataSource(defaultTargetDataSource); } this.targetDataSources.putAll(targetDataSources); } // 获取数据源名称,采用轮询的方式实现 @Override protected Object determineCurrentLookupKey() { DataChooseParam dataSource = DataSourceHolder.getDataSource(); DataSourceHolder.clear(); if (null == dataSource) { throw new IllegalArgumentException("Property 'determineCurrentLookupKey' is DataSourceHolder.getDataSource() not dataSource:[" + dataSource + "]"); } DataSourceGroup dataSourceGroup = DataSourceGroupNameEnum.getDataSourceGroupByName(dataSource.getGroupName()); if (null == dataSourceGroup) { throw new IllegalArgumentException("Property 'determineCurrentLookupKey' is DataSourceGroupNameEnum.getDataSourceGroupByName() not dataSourceGroup:[" + dataSourceGroup + "]"); } String dataSourceKey = null; // Map<String,List<String>> map = dataSourceGroupMap.get(dataSource.getGroupName()); DataSourceEnum dataEnum = dataSource.getDataType(); if (null == dataEnum) { dataEnum = DataSourceEnum.MASTER; } if (DataSourceEnum.MASTER == dataSource.getDataType()) { dataSourceKey = dataSourceGroup.getMasterSources(); dataSource.setDataClient(dataSourceKey); return dataSourceKey; } if (DataSourceEnum.SLAVE == dataSource.getDataType()) { List<String> list = dataSourceGroup.getSlaveSources(); if (null == list || list.isEmpty()) { dataSourceKey = dataSourceGroup.getMasterSources(); dataSource.setDataClient(dataSourceKey); return dataSourceKey; } // throw new IllegalArgumentException("Property 'determineCurrentLookupKey' is DataSourceHolder.map.get(dataType) list isEmpty or null "); int size = list.size(); if (size == 1) { dataSourceKey = list.get(0); } else { AtomicInteger counter = currentMap.get(dataEnum.name()); if (counter == null) { counter = new AtomicInteger(0); currentMap.put(dataEnum.name(), counter); } else { if (counter.incrementAndGet() >= size) { counter.set(0); } } dataSourceKey = list.get(counter.intValue()); } } dataSource.setDataClient(dataSourceKey); return dataSourceKey; }
本例中,咱们建立一个接口,而后建立该接口的2个实现类,分别命名不一样的名字,而后在须要注入的地方使用@Qualifier 指定注入对应的实例。数据库
package top.suven.core.db; import com.alibaba.druid.pool.DruidAbstractDataSource; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.*; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.*; import org.springframework.core.env.Environment; import javax.sql.DataSource; import java.util.*; /** * 应用服务起动类,加载数据源实现模块组实现类 * 数据库聚群,启动和管理的配置实现类; * datasource_druid_config_enabled =true.表示启动该实现类来初始化DataSourceGroupNameEnum对象管理的数据源, * 默认值为false, * 数据库的初始参数都是统一的,实现类为DruidDataConfig,统一从Environment environment 初始化获取; * * setEnvironment()-->postProcessBeanDefinitionRegistry() --> postProcessBeanFactory() */ @Configuration @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DruidDataSourceAutoConfigure.class,JdbcTemplateAutoConfiguration.class}) @ConditionalOnProperty(name = DataDruidConfig.datasource_druid_config_enabled, matchIfMissing = false) public class DataSourceAutoConfig implements DataDruidConfig, EnvironmentAware, BeanDefinitionRegistryPostProcessor,ApplicationContextAware { private final Logger logger = LoggerFactory.getLogger(DataSourceAutoConfig.class); private RelaxedPropertyResolver property; private DruidDataConfig druidConfig; private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void setEnvironment(Environment environment) { this.property = new RelaxedPropertyResolver(environment); druidConfig = new DruidDataConfig(); logger.warn("DataSourceAutoConfiguration in DruidDataConfig info=[{}]", druidConfig.toString()); } @Bean("dataSource") public DataSource routingDataSource() { DynamicDataSource dataSource = new DynamicDataSource(); Set<DataSourceGroupNameEnum> sourceNames = DataSourceGroupNameEnum.getSourceNames(); if (sourceNames == null || sourceNames.isEmpty()) { throw new RuntimeException("DynamicDataSource init DataSource isEmpty "); } for (DataSourceGroupNameEnum dataName : sourceNames) { dataSource.initDataByGroup(dataName,applicationContext); } dataSource.setTargetDataSources(); logger.warn("Dynamic DataSource Registry --- routingDataSource Successfully ... "); return dataSource; } /** * 将 DataSourceGroupNameEnum 数据源组 生成bean对象注入到spring管理容器中; * 经过 datasource_druid_enabled 对应模块数据库配置总开关,默认值为true * 经过 datasource_druid_slave_enabled 对应模块从数据库配置总开关,默认值为true * 将模块初始化后数据源信息,并将结果生成spring bean 名称缓存到DataSourceGroupNameEnum对象中 * @param registry * @throws BeansException */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { Set<DataSourceGroupNameEnum> sourceNames = null; sourceNames = DataSourceGroupNameEnum.getSourceNames(); if (sourceNames == null || sourceNames.isEmpty()) { return; } boolean isPrimary = true; //从数据库配置总开关, boolean dataSourceSlaveEnabled = property.getProperty( String.format(datasource_druid_slave_enabled,datasource_druid_frame,datasource_druid_slave).trim(), Boolean.class,true); for (DataSourceGroupNameEnum dataName : sourceNames) { //模块数据库 主-从 配置总开关, boolean moduleDataSourceEnabled = property.getProperty( String.format(datasource_druid_master_enabled,dataName.getValue(),datasource_druid_master).trim(), Boolean.class,true); //模块从数据库配置总开关, boolean moduleDataSourceSlaveEnabled = property.getProperty( String.format(datasource_druid_slave_enabled,dataName.getValue(),datasource_druid_slave).trim(), Boolean.class,true); /** * 经过模块对应的配置文件获取主数据库信息,若是不存在就跳该模块的对应的全部数据库 */ String url = property.getProperty(String.format(datasource_druid_url,dataName.getValue(),datasource_druid_master)); if (null == url || "".equals(url) || !moduleDataSourceEnabled) { continue; } String username = property.getProperty(String.format(datasource_druid_username,dataName.getValue(),datasource_druid_master).trim()); String password = property.getProperty(String.format(datasource_druid_password,dataName.getValue(),datasource_druid_master).trim()); /** * 注入到spring bean的名称生成规则;(模块文称+ MasterDataSource) */ String datasourceMasterBeanName = dataName.getValue() + datasource_master_name ; BeanDefinitionBuilder datasourceFactory = initDatasourceBean(druidConfig,url,username,password); BeanDefinition beanDefinition = datasourceFactory.getBeanDefinition(); if(isPrimary){//设置惟一主数据库 beanDefinition.setPrimary(true); isPrimary = false; } registry.registerBeanDefinition(datasourceMasterBeanName, beanDefinition); List<String> slaveDataSources = new ArrayList<>(); int i = 0 ; while (dataSourceSlaveEnabled && moduleDataSourceSlaveEnabled){ String slave = i == 0 ? datasource_druid_slave : datasource_druid_slave + i; /** * 注入到spring bean的名称生成规则;(模块文称+ SlaveDataSource + 序列号1,2,3...) */ String datasourceSlaveBeanName = dataName.getValue() + datasource_slave_name + i; url = property.getProperty(String.format(datasource_druid_url,dataName.getValue(),slave).trim()); if (null == url || "".equals(url)) { break; } username = property.getProperty(String.format(datasource_druid_username,dataName.getValue(),slave).trim()); password = property.getProperty(String.format(datasource_druid_password,dataName.getValue(),slave).trim()); datasourceFactory = initDatasourceBean(druidConfig,url,username,password); registry.registerBeanDefinition(datasourceSlaveBeanName, datasourceFactory.getBeanDefinition()); slaveDataSources.add(datasourceSlaveBeanName); i++; } /** * 将模块初始化后数据源信息,并将结果生成spring bean 名称缓存到DataSourceGroupNameEnum 对象中 */ DataSourceGroupNameEnum.setDataSource(dataName,datasourceMasterBeanName,slaveDataSources); logger.warn("DataSourceAutoConfig postProcessBeanDefinitionRegistry Registry --- dataSourceName[{}] Successfully ...",datasourceMasterBeanName); } } /** * 初始化DruidDataSource对象 * 经过BeanDefinitionBuilder生成DruidDataSource对象实现类 * 而且经过配置文件获取对应的指定属性 * @param url * @param username * @param password * @return */ private BeanDefinitionBuilder initDatasourceBean(DruidDataConfig druid,String url,String username,String password){ BeanDefinitionBuilder datasourceFactory = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class); datasourceFactory.setLazyInit(true); //设置是否懒加载 datasourceFactory.setScope(BeanDefinition.SCOPE_SINGLETON); //设置scope,为单例类型 datasourceFactory.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); //设置是否能够被其余对象自动注入 datasourceFactory.addPropertyValue(URL, url); datasourceFactory.addPropertyValue(USERNAME, username); datasourceFactory.addPropertyValue(PASSWORD, password); initDataSource(datasourceFactory,druid); return datasourceFactory; } // /** * 初始化数据库的默认配置参数; * @param datasourceFactory */ private void initDataSource(BeanDefinitionBuilder datasourceFactory,DruidDataConfig druid){ datasourceFactory.addPropertyValue(INITIALSIZE, druid.getInitialSize()); datasourceFactory.addPropertyValue(MINIDLE, druid.getMinIdle()); datasourceFactory.addPropertyValue(MAXACTIVE, druid.getMaxActive()); datasourceFactory.addPropertyValue(MAXWAIT, druid.getMaxWait()); datasourceFactory.addPropertyValue(TIME_BETWEENE_VICTION_RUNS_MILLIS,druid.getTimeBetweenEvictionRunsMillis() ); datasourceFactory.addPropertyValue(MIN_EVICTABLE_IDLE_TIME_MILLIS, druid.getMinEvictableIdleTimeMillis() ); datasourceFactory.addPropertyValue(VALIDATION_QUERY, druid.getValidationQuery()); datasourceFactory.addPropertyValue(TEST_WHILEIDLE, druid.isTestWhileIdle()); datasourceFactory.addPropertyValue(TEST_ON_BORROW, druid.isTestOnBorrow()); datasourceFactory.addPropertyValue(TEST_ON_RETURN, druid.isTestOnReturn()); datasourceFactory.addPropertyValue(POOL_PREPARED_STATEMENTS, druid.isPoolPreparedStatements()); try { datasourceFactory.addPropertyValue(FILTERS,druid.getFilters()); } catch (Exception e) { logger.error("druid configuration initialization filter", e); } } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } private class DruidDataConfig { private int initialSize; private int minIdle; private int maxActive; private int maxWait; private long timeBetweenEvictionRunsMillis; private long minEvictableIdleTimeMillis; private String validationQuery; private boolean testWhileIdle; private boolean testOnBorrow; private boolean testOnReturn; private boolean poolPreparedStatements; private String filters; public DruidDataConfig(){ this.setInitialSize(property.getProperty(datasource_druid_initialSize,Integer.class,DruidAbstractDataSource.DEFAULT_INITIAL_SIZE)); this.setMinIdle(property.getProperty(datasource_druid_minIdle,Integer.class,DruidAbstractDataSource.DEFAULT_MIN_IDLE)); this.setMaxActive(property.getProperty(datasource_druid_maxActive,Integer.class,DruidAbstractDataSource.DEFAULT_MAX_ACTIVE_SIZE)); this.setMaxWait(property.getProperty(datasource_druid_maxWait,Integer.class,DruidAbstractDataSource.DEFAULT_MAX_WAIT)); this.setTimeBetweenEvictionRunsMillis(property.getProperty(datasource_druid_timeBetweenEvictionRunsMillis,Long.class,DruidAbstractDataSource.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS)); this.setMinEvictableIdleTimeMillis(property.getProperty(datasource_druid_minEvictableIdleTimeMillis,Long.class,DruidAbstractDataSource.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS)); this.setValidationQuery(property.getProperty(datasource_druid_validationQuery,DruidAbstractDataSource.DEFAULT_VALIDATION_QUERY)); this.setTestWhileIdle(property.getProperty(datasource_druid_testWhileIdle,Boolean.class,true)); this.setTestOnBorrow(property.getProperty(datasource_druid_testOnBorrow,Boolean.class)); this.setTestOnReturn(property.getProperty(datasource_druid_testOnReturn,Boolean.class)); this.setPoolPreparedStatements(property.getProperty(datasource_druid_poolPreparedStatements,Boolean.class)); this.setFilters(property.getProperty(datasource_druid_filters,"")); } public int getInitialSize() { return initialSize; } public void setInitialSize(int initialSize) { this.initialSize = initialSize; } public int getMinIdle() { return minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxActive() { return maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public int getMaxWait() { return maxWait; } public void setMaxWait(int maxWait) { this.maxWait = maxWait; } public long getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; } public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; } public long getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; } public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public boolean isTestWhileIdle() { return testWhileIdle; } public void setTestWhileIdle(boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } public boolean isTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public boolean isTestOnReturn() { return testOnReturn; } public void setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; } public boolean isPoolPreparedStatements() { return poolPreparedStatements; } public void setPoolPreparedStatements(boolean poolPreparedStatements) { this.poolPreparedStatements = poolPreparedStatements; } public String getFilters() { return filters; } public void setFilters(String filters) { this.filters = filters; } @Override public String toString() { return "DruidDataConfig{" + "initialSize=" + initialSize + ", minIdle=" + minIdle + ", maxActive=" + maxActive + ", maxWait=" + maxWait + ", timeBetweenEvictionRunsMillis=" + timeBetweenEvictionRunsMillis + ", minEvictableIdleTimeMillis=" + minEvictableIdleTimeMillis + ", validationQuery='" + validationQuery + '\'' + ", testWhileIdle=" + testWhileIdle + ", testOnBorrow=" + testOnBorrow + ", testOnReturn=" + testOnReturn + ", poolPreparedStatements=" + poolPreparedStatements + ", filters='" + filters + '\'' + '}'; } } }
package top.suven.core.db; import java.util.ArrayList; import java.util.List; /** * Created by suven on 16/8/30. * 封装每一个项目的数据源对象; */ public class DataSourceGroup { private String groupName; private String masterSources; private List<String> slaveSources = new ArrayList<>(); private String masterMethod; private String slaveMethod; public String getMasterSources() { return masterSources; } public void setMasterSources(String masterSources) { this.masterSources = masterSources; } public List<String> getSlaveSources() { return slaveSources; } public void setSlaveSources(List<String> slaveSources) { this.slaveSources = slaveSources; } public void setSlaveSources(String slaveKey) { if(this.slaveSources == null){ slaveSources = new ArrayList<>(); } this.slaveSources.add(slaveKey); } public String getMasterMethod() { return masterMethod; } public void setMasterMethod(String masterMethod) { this.masterMethod = masterMethod; } public String getSlaveMethod() { return slaveMethod; } public void setSlaveMethod(String slaveMethod) { this.slaveMethod = slaveMethod; } public String getGroupName() { return groupName; } public DataSourceGroup setGroupName(String groupName) { this.groupName = groupName; return this; } }
package top.suven.core.db; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * 项目数据源名称泛型实现类, * 用来实现多数据源自动切换使用,配合 DataSourceAutoConfiguration 启动初化数据源注入到spring bean 中 * 建立spring DynamicDataSource bean 对象,并注入到spring容器中,命名为dataSource; * * */ public enum DataSourceGroupNameEnum { DATA_NAME_USER("user"),//用户组数据库,包括主和从数据库 DATA_NAME_OAUTH("oauth"),//验证组数据库,包括主和从数据库 DATA_NAME_ASSETS("assets"),//用户资产组数据库,包括主和从数据库 //随着项目和数据的增长多,在型这里增长属性便可 ; public static Logger logger = LoggerFactory.getLogger(DataSourceGroupNameEnum.class); private String value; private static Map<String, DataSourceGroupNameEnum> tbTypeMap = new LinkedHashMap<>(); private static Map<String,DataSourceGroup> groupMap = new ConcurrentHashMap<>(); static { for(DataSourceGroupNameEnum type : values()) { tbTypeMap.put(type.name(), type); groupMap.put(type.name(), new DataSourceGroup().setGroupName(type.name())); } } DataSourceGroupNameEnum(String value) { this.value = value; } public String getValue() { return value; } private static DataSourceGroupNameEnum getName(String name){ return tbTypeMap.get(name); } public static Collection<DataSourceGroupNameEnum> getValues(){ return tbTypeMap.values(); } public static Set<DataSourceGroupNameEnum> getSourceNames(){ Set<DataSourceGroupNameEnum> sourceNames = new LinkedHashSet<>(); sourceNames.addAll(tbTypeMap.values()); return sourceNames; } //经过组名,获取数据源集合对象; public static DataSourceGroup getDataSourceGroupByName(String groupName){ if(null == groupName ){ logger.warn("DataSourceGroup getDataSourceGroupByName by groupName[{}]", groupName); return null; } DataSourceGroupNameEnum dataSourceGroupNameEnum = getName(groupName); return getDataSourceGroupByName(dataSourceGroupNameEnum); } //经过组名,获取数据源集合对象; public static DataSourceGroup getDataSourceGroupByName(DataSourceGroupNameEnum groupNameEnum){ if( null == groupNameEnum){ logger.warn("DataSourceGroup init getDataSourceGroupByName by groupNameEnum[{}]", groupNameEnum); return null; } DataSourceGroup dataSourceGroup = groupMap.get(groupNameEnum.name()); return dataSourceGroup; } //将数据源对象添加到组名管理 public static void setSlaveDataSourceKey(DataSourceGroupNameEnum groupNameEnum, String... datasourceKey){ DataSourceGroup dataSourceGroup = groupMap.get(groupNameEnum.name()); if( null == dataSourceGroup){ logger.warn("DataSourceGroup init setMasterDataSourceKey by dataSourceGroup groupNameEnum[{}]", dataSourceGroup); return ; } if(datasourceKey.length == 1){ dataSourceGroup.setSlaveSources(datasourceKey[0]); }else{ dataSourceGroup.setSlaveSources(Arrays.asList(datasourceKey)); } } public static void setDataSource(DataSourceGroupNameEnum groupNameEnum,String masterDatasourceKey, List<String> slaveDatasources ){ DataSourceGroup dataSourceGroup = groupMap.get(groupNameEnum.name()); if( null == dataSourceGroup){ logger.warn("DataSourceGroup init setMasterDataSourceKey by dataSourceGroup groupNameEnum[{}]", dataSourceGroup); return ; } dataSourceGroup.setMasterSources(masterDatasourceKey); if(slaveDatasources != null){ dataSourceGroup.setSlaveSources(slaveDatasources); } } //将数据源对象添加到组名管理 public static void setDataSourceKey(DataSourceGroupNameEnum groupNameEnum,String masterDatasourceKey, String... slaveDatasourceKey){ List<String> slaveList = new ArrayList(); if( slaveDatasourceKey != null){ slaveList = Arrays.asList(slaveDatasourceKey); } setDataSource(groupNameEnum,masterDatasourceKey,slaveList); } }
package top.suven.core.db; /** * Created by suven on 16/9/7. */ public class DataChooseParam { private boolean isRotate; private String groupName; private DataSourceEnum dataType; // 入参数为: MASTER 或 SLAVE; private String dataClient; public DataChooseParam(){ } public DataChooseParam(String groupName) { this.groupName = groupName; this.isRotate = true; } public DataChooseParam(String groupName, DataSourceEnum dataType) { this.groupName = groupName; this.dataType = dataType; this.isRotate = true; } public boolean isRotate() { return isRotate; } public String getGroupName() { return groupName; } public DataSourceEnum getDataType() { return dataType; } public String getDataClient() { return dataClient; } public void setDataClient(String dataClient) { this.dataClient = dataClient; } @Override public String toString() { return "DataChooseParam{" + "isRotate=" + isRotate + ", groupName='" + groupName + '\'' + // ", dataType='" + dataType + '\'' + ", dataClient='" + dataClient + '\'' + '}'; } }
package top.suven.core.db; /** * @author suven.wang * @version * * 经过 ThreadLocal 当前线程安全类,实现线程内,参数切换实现业务解偶; * 以达到动态数据库链接池实现切换,实现多数据源原理 */ public class DataSourceHolder { // 数据源名称线程池 private static final ThreadLocal<DataChooseParam> holder = new ThreadLocal<>(); public static void putDataSource(DataChooseParam dataChooseParam) { holder.set(dataChooseParam); } public static DataChooseParam getDataSource() { return holder.get(); } public static void clear() { holder.remove(); } }
package top.suven.core.db; public enum DataSourceEnum{ MASTER, SLAVE; }
#-----------------------------------datasource-------------------------------------- # 是否启动动态加载数据库源,默认是false,不启动该实现数据源 suven.datasource.druid.config.enabled=true # 是否启动动态加载全部从数据库源,默认是true,支持动态读写分离 #suven.datasource.druid.slave.enabled=true suven.datasource.druid.initialize=true suven.datasource.druid.dbType= postgresql suven.datasource.druid.type = com.alibaba.druid.pool.DruidDataSource suven.datasource.druid.driverClassName =org.postgresql.Driver suven.datasource.druid.filters = stat suven.datasource.druid.maxActive = 20 suven.datasource.druid.initialSize = 5 suven.datasource.druid.maxWait = 60000 suven.datasource.druid.minIdle = 5 suven.datasource.druid.timeBetweenEvictionRunsMillis = 60000 suven.datasource.druid.minEvictableIdleTimeMillis = 300000 suven.datasource.maxPoolPreparedStatementPerConnectionSize=1 suven.datasource.druid.validationQuery = select 'x'; suven.datasource.druid.testWhileIdle = true suven.datasource.druid.testOnBorrow = false suven.datasource.druid.testOnReturn = false suven.datasource.druid.poolPreparedStatements = true suven.datasource.druid.maxOpenPreparedStatements = 200 spring.datasource.druid.stat-view-servlet.enabled=false spring.datasource.druid.statViewServlet.urlPattern=/druid/* # \u767D\u540D\u5355\uFF1A #spring.datasource.druid.statViewServlet.allow= # IP\u9ED1\u540D\u5355 (\u5B58\u5728\u5171\u540C\u65F6\uFF0Cdeny\u4F18\u5148\u4E8Eallow) : \u5982\u679C\u6EE1\u8DB3deny\u7684\u8BDD\u63D0\u793A:Sorry, you are not permitted to view this page. #spring.datasource.druid.statViewServlet.deny= spring.datasource.druid.statViewServlet.loginUsername=admin spring.datasource.druid.statViewServlet.loginPassword=123456 # \u662F\u5426\u80FD\u591F\u91CD\u7F6E\u6570\u636E. spring.datasource.druid.statViewServlet.resetEnable=false spring.datasource.druid.web-stat-filter.enabled=false spring.datasource.druid.webStatFilter.sessionStatEnable=false spring.datasource.druid.webStatFilter.profileEnable=false spring.datasource.druid.webStatFilter.urlPattern=/* spring.datasource.druid.webStatFilter.exclusions="*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,/server/druid/* # \u914D\u7F6E\u65E5\u5FD7\u8F93\u51FA spring.datasource.druid.filter.slf4j.enabled=false spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false # \u914D\u7F6E\u76D1\u63A7\u7EDF\u8BA1\u62E6\u622A\u7684filters\uFF0C\u53BB\u6389\u540E\u76D1\u63A7\u754C\u9762sql\u65E0\u6CD5\u7EDF\u8BA1\uFF0C'wall'\u7528\u4E8E\u9632\u706B\u5899 spring.datasource.filters=stat,wall,log4j # \u901A\u8FC7connectProperties\u5C5E\u6027\u6765\u6253\u5F00mergeSql\u529F\u80FD\uFF1B\u6162SQL\u8BB0\u5F55 spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # \u5408\u5E76\u591A\u4E2ADruidDataSource\u7684\u76D1\u63A7\u6570\u636E spring.datasource.useGlobalDataSourceStat=true spring.filter.dos.urlPatterns=/server/* spring.filter.dos.exclusions=/druid/*,/server/druid/* #-------------------------------------END-------------------------------------- # 根据项目须要增长数据源,减轻服务器数据库压力,只须要根据规则配置从数据源,并重启项目便可...(,1,2),其它模块也同样 #---------------------------------ASSETS--------------------------------# #suven.datasource.assets.master.enabled=false ## 关闭指定模块的master主数据源,默认值为true #suven.datasource.assets.slave.enabled=false ## 关闭指定模块的全部slave从数据源,默认值为true #master suven.datasource.assets.master.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.assets.master.username = test suven.datasource.assets.master.password = test #slave suven.datasource.assets.slave.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.assets.slave.username = test suven.datasource.assets.slave.password = test #slave1 suven.datasource.assets.slave1.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.assets.slave1.username = test suven.datasource.assets.slave1.password = test #slave2 suven.datasource.assets.slave2.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.assets.slave2.username = test suven.datasource.assets.slave2.password = test #slave3 suven.datasource.assets.slave3.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.assets.slave3.username = test suven.datasource.assets.slave3.password = test #----------------------------------END----------------------------------# #---------------------------------OAUTH--------------------------------# #suven.datasource.oauth.master.enabled=false ## 关闭指定模块的master主数据源,默认值为true #suven.datasource.oauth.slave.enabled=false ## 关闭指定模块的全部slave从数据源,默认值为true #master suven.datasource.oauth.master.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.oauth.master.username = test suven.datasource.oauth.master.password = test #slave suven.datasource.oauth.slave.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.oauth.slave.username = test suven.datasource.oauth.slave.password = test #slave1 suven.datasource.oauth.slave1.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.oauth.slave1.username = test suven.datasource.oauth.slave1.password = test #----------------------------------END-----------------------------------# #---------------------------------USER--------------------------------# #suven.datasource.user.master.enabled=false ## 关闭指定模块的master主数据源,默认值为true #suven.datasource.user.slave.enabled=false ## 关闭指定模块的全部slave从数据源,默认值为true #master suven.datasource.user.master.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.user.master.username = test suven.datasource.user.master.password = test #slave suven.datasource.user.slave.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.user.slave.username = test suven.datasource.user.slave.password = test #slave1 suven.datasource.user.slave1.url = jdbc:mysql://127.0.0.1:3306/datasource_name?autoReconnect=true&characterEncoding=utf-8 suven.datasource.user.slave1.username = test suven.datasource.user.slave1.password = test #----------------------------------END----------------------------------#