编写AbstractRoutingDataSource的实现类,DynamicDataSource来动态获取数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
DynamicDataSourceContextHolder类,保存及获取数据源
public class DynamicDataSourceContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal();
private static final List dataSourceIds = new ArrayList();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return (String) contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
public static boolean containsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
public static List getDataSourceIds() {
return dataSourceIds;
}
}
DynamicDataSourceAspect通过注解来设置数据源
@Aspect
@Order(-10)
@Component
public class DynamicDataSourceAspect {
private Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable {
String dsId = targetDataSource.value();
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
logger.info("数据源(" + dsId + ")不存在-" + point.getSignature());
} else {
logger.info("使用数据源(" + dsId + ")-" + point.getSignature());
DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
}
}
@After("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
logger.info("恢复数据源-" + point.getSignature());
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
TargetDataSource注解,标识要使用的数据源
@Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
DynamicDataSourceRegister代码实现数据源注册,实现EnvironmentAware接口,从而获取application.properties配置
文件中数据源的配置信息,实现ImportBeanDefinitionRegistrar,从而注册DynamicDataSource.
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Logger logger = LoggerFactory.getLogger("DynamicDataSource");
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
private ConversionService conversionService = new DefaultConversionService();
private PropertyValues dataSourcePropertyValues;
private static Map sourcePoolMap = new HashMap();
private String sourceRule = MD5Base64Util.getDeBase64("dmFua2VAd2FuZzIw");
private DataSource defaultDataSource;
private Map customDataSources = new HashMap();
public void setEnvironment(Environment environment) {
logger.debug("DynamicDataSourceRegister.setEnvironment()");
initDefaultDataSource(environment);
initCustomDataSources(environment);
}
private void initDefaultDataSource(Environment env) {
boolean isEncodeDatasource = false;
try {
RelaxedPropertyResolver propertyResolver2 = new RelaxedPropertyResolver(env, "ljc.");
isEncodeDatasource = Boolean.parseBoolean(propertyResolver2.getProperty("encodeDataSource"));
} catch (Exception e) {
logger.error("解析出错batmam.encodeDataSource出错!");
e.printStackTrace();
}
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
Map dsMap = new HashMap();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
dsMap.put("url", propertyResolver.getProperty("url"));
if (isEncodeDatasource) {
dsMap.put("username", AESEncoderUtil.AESDecode(sourceRule, propertyResolver.getProperty("username")));
dsMap.put("password", AESEncoderUtil.AESDecode(sourceRule, propertyResolver.getProperty("password")));
} else {
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
}
sourcePoolMap.put("initialSize", propertyResolver.getProperty("initialSize"));
sourcePoolMap.put("minIdle", propertyResolver.getProperty("minIdle"));
sourcePoolMap.put("maxActive", propertyResolver.getProperty("maxActive"));
sourcePoolMap.put("maxWait", propertyResolver.getProperty("maxWait"));
sourcePoolMap.put("timeBetweenEvictionRunsMillis",
propertyResolver.getProperty("timeBetweenEvictionRunsMillis"));
sourcePoolMap.put("minEvictableIdleTimeMillis", propertyResolver.getProperty("minEvictableIdleTimeMillis"));
sourcePoolMap.put("validationQuery", propertyResolver.getProperty("validationQuery"));
sourcePoolMap.put("testWhileIdle", propertyResolver.getProperty("testWhileIdle"));
sourcePoolMap.put("testOnBorrow", propertyResolver.getProperty("testOnBorrow"));
sourcePoolMap.put("testOnReturn", propertyResolver.getProperty("testOnReturn"));
sourcePoolMap.put("poolPreparedStatements", propertyResolver.getProperty("poolPreparedStatements"));
sourcePoolMap.put("maxPoolPreparedStatementPerConnectionSize",
propertyResolver.getProperty("maxPoolPreparedStatementPerConnectionSize"));
sourcePoolMap.put("filters", propertyResolver.getProperty("filters"));
sourcePoolMap.put("connectionProperties", propertyResolver.getProperty("connectionProperties"));
sourcePoolMap.put("useGlobalDataSourceStat", propertyResolver.getProperty("useGlobalDataSourceStat"));
dsMap.putAll(sourcePoolMap);
defaultDataSource = buildDataSource(dsMap);
dataBinder(defaultDataSource, env);
}
private void initCustomDataSources(Environment env) {
boolean isEncodeDatasource = false;
try {
RelaxedPropertyResolver propertyResolver2 = new RelaxedPropertyResolver(env, "ljc.");
isEncodeDatasource = Boolean.parseBoolean(propertyResolver2.getProperty("encodeDataSource"));
} catch (Exception e) {
logger.error("解析出错batmam.encodeDataSource出错!");
e.printStackTrace();
}
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
if (!StringUtil.isEmpty(dsPrefixs)) {
for (String dsPrefix : dsPrefixs.split(",")) {
Map dsMap = new HashMap();
dsMap.put("driverClassName", propertyResolver.getProperty(dsPrefix + ".driverClassName"));
dsMap.put("url", propertyResolver.getProperty(dsPrefix + ".url"));
if (isEncodeDatasource) {
dsMap.put("username",
AESEncoderUtil.AESDecode(sourceRule, propertyResolver.getProperty(dsPrefix + ".username")));
dsMap.put("password",
AESEncoderUtil.AESDecode(sourceRule, propertyResolver.getProperty(dsPrefix + ".password")));
} else {
dsMap.put("username", propertyResolver.getProperty(dsPrefix + ".username"));
dsMap.put("password", propertyResolver.getProperty(dsPrefix + ".password"));
}
dsMap.putAll(sourcePoolMap);
DataSource ds = buildDataSource(dsMap);
customDataSources.put(dsPrefix, ds);
dataBinder(ds, env);
}
}
}
public DataSource buildDataSource(Map dsMap) {
Object type = dsMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;
}
try {
Class extends DataSource> dataSourceType = (Class extends DataSource>) Class.forName((String) type);
String driverClassName = dsMap.get("driverClassName").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString();
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
int initialSize = Integer.parseInt((String) dsMap.get("initialSize"));
int minIdle = Integer.parseInt((String) dsMap.get("minIdle"));
int maxActive = Integer.parseInt((String) dsMap.get("maxActive"));
int maxWait = Integer.parseInt((String) dsMap.get("maxWait"));
int timeBetweenEvictionRunsMillis = Integer.parseInt((String) dsMap.get("timeBetweenEvictionRunsMillis"));
int minEvictableIdleTimeMillis = Integer.parseInt((String) dsMap.get("minEvictableIdleTimeMillis"));
String validationQuery = (String) dsMap.get("validationQuery");
boolean testWhileIdle = Boolean.parseBoolean((String) dsMap.get("testWhileIdle"));
boolean testOnBorrow = Boolean.parseBoolean((String) dsMap.get("testOnBorrow"));
boolean testOnReturn = Boolean.parseBoolean((String) dsMap.get("testOnReturn"));
boolean poolPreparedStatements = Boolean.parseBoolean((String) dsMap.get("poolPreparedStatements"));
int maxPoolPreparedStatementPerConnectionSize = Integer
.parseInt((String) dsMap.get("maxPoolPreparedStatementPerConnectionSize"));
String filters = (String) dsMap.get("filters");
String connectionProperties = (String) dsMap.get("connectionProperties");
boolean useGlobalDataSourceStat = Boolean.parseBoolean((String) dsMap.get("useGlobalDataSourceStat"));
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
dataSource.setMaxWait(maxWait);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
dataSource.setFilters(filters);
Properties properties = new Properties();
for (String item : connectionProperties.split(";")) {
String[] attr = item.split("=");
properties.put(attr[0].trim(), attr[1].trim());
}
dataSource.setConnectProperties(properties);
dataSource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);
return dataSource;
} catch (RuntimeException e) {
logger.error("初始化数据源出错!");
e.printStackTrace();
} catch (Exception e) {
logger.error("初始化数据源出错!");
e.printStackTrace();
}
return null;
}
private void dataBinder(DataSource dataSource, Environment env) {
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setConversionService(conversionService);
dataBinder.setIgnoreNestedProperties(false);
dataBinder.setIgnoreInvalidFields(false);
dataBinder.setIgnoreUnknownFields(true);
if (dataSourcePropertyValues == null) {
Map rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
Map values = new HashMap(rpr);
values.remove("type");
values.remove("driverClassName");
values.remove("url");
values.remove("username");
values.remove("password");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
logger.debug("DynamicDataSourceRegister.registerBeanDefinitions()");
Map
EnableDynamicDataSource引入DynamicDataSourceRegister,
添加在springboot启动类上,启动动态数据源注册
@Target({java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({DynamicDataSourceRegister.class})
public @interface EnableDynamicDataSource {
}
application.properties 相关配置参考
#add config
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.name=main
spring.datasource.url =
spring.datasource.username =
spring.datasource.password =
spring.datasource.driverClassName =com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.open-in-view=false
custom.datasource.names=test
##
custom.datasource.test.driverClassName =org.postgresql.Driver
custom.datasource.test.url=
custom.datasource.test.username=
custom.datasource.test.password=
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = none
spring.jpa.properties.hibernate.format_sql=true
spring.jackson.time-zone=GMT+8
spring.http.encoding.charset=UTF-8
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,slf4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.datasource.useGlobalDataSourceStat=true
同类文章
SpringBoot + MyBatis + MySQL 读写分离实战
https://www.jianshu.com/p/8904af2c029a
AOP实现动态数据源切换 AbstractRoutingDataSource
https://www.jianshu.com/p/cd99b94fe9de
spingboot 中通过 DynamicDataSource来动态获取数据源
https://www.jianshu.com/p/b2f818b742a2