总体思路
使用AOP进行数据源切换,继承AbstractRoutingDataSource实现数据源动态的获取,使用注解指定数据源。
//指定aop事务执行顺序,已保证在切换数据源的后面
@EnableTransactionManagement(order = 2)
//排除数据源自动配置
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
一. 多数据源配置
spring:
datasource:
# 使用druid数据源
druid:
master:
url: jdbc:mysql:/url/database?useUnicode=true&characterEncoding=utf-8
username: xx
password: 123344
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 300
initialSize: 5
maxWait: 60000
minIdle: 5
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
WebStatFilter:
enabled: true #是否启用StatFilter默认值true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/
StatViewServlet:
enabled: true # 是否启用StatViewServlet默认值true
urlPattern: /druid/*
slave:
url: jdbc:mysql://url/database1?useUnicode=true&characterEncoding=utf-8
username: xxx
password: 432423
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 300
initialSize: 5
maxWait: 60000
minIdle: 5
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
WebStatFilter:
enabled: true #是否启用StatFilter默认值true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid1/
StatViewServlet:
enabled: true # 是否启用StatViewServlet默认值true
urlPattern: /druid1/*
aop:
proxy-target-class: true
读取配置
package com.***.dataSource;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 动态数据源配置
* @author Lucian
*/
@Configuration
public class DynamicDataSourceConfiguration {
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.master")
public DataSource masterDataSource(){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setName("masterDataSource");
return dataSource;
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.slave")
public DataSource slaveDataSource(){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setName("slaveDataSource");
return dataSource;
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Bean(name = "dynamicDataSource")
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSourceOne());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap(2);
dsMap.put("masterDataSource", masterDataSource());
dsMap.put("slaveDataSource", slaveDataSource());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
@Bean
public PlatformTransactionManager txManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
@Bean
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
return sqlSessionFactoryBean;
}
@Bean
public SqlSessionFactory sqlSessionFactory(SqlSessionFactoryBean sqlSessionFactoryBean) throws Exception {
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory); // 使用上面配置的Factory
return template;
}
}
二. 数据源动态切换类
package com.hunter.nocardpay.core.dataSource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 动态数据源AOP切面
*/
@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {
@Around("execution(* com.hunter.nocardpay.*.service..*(..))")
public Object switchDS(ProceedingJoinPoint point) throws Throwable {
Class<?> className = point.getTarget().getClass();
String dataSource = DataSourceConsts.DEFAULT;
if (className.isAnnotationPresent(DS.class)) {
DS ds = className.getAnnotation(DS.class);
dataSource = ds.value();
}else{
// 得到访问的方法对象
String methodName = point.getSignature().getName();
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(DS.class)) {
DS annotation = method.getAnnotation(DS.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
try {
return point.proceed();
} finally {
DataSourceContextHolder.clearDB();
}
}
}
三. 当前线程数据源
package com.hunter.nocardpay.core.dataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 当前线程数据源
* @author Lucian
*/
public class DataSourceContextHolder {
public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
log.debug("切换到{}数据源", dbType);
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
四. 动态数据源
package com.hunter.nocardpay.core.dataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源
* @author Lucian
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
log.debug("数据源为:====", DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
数据源注解
package com.hunter.nocardpay.core.dataSource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据源注解
* @author Lucian
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DS {
String value() default "masterDataSource";
}