开发场景中经常会遇到多数据源的情况,比如MySQL读写分离等,下面是通过springboot+AOP实现.需要的maven依赖
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-aop
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.0
com.alibaba
druid
1.0.31
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.0
mysql
mysql-connector-java
com.github.noraui
ojdbc7
12.1.0.2
yml配置:
server:
port: 8080
spring:
datasource:
master:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
max-idle: 10
max-wait: 10000
min-idle: 5
initial-size: 5
slave:
url: jdbc:mysql://localhost:3307/test
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
max-idle: 10
max-wait: 10000
min-idle: 5
initial-size: 5
关键的配置文件目录:
DynamicDataSourceHolder:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DynamicDataSourceHolder {
private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceHolder.class);
private static final ThreadLocal contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
logger.info("切换到{"+dbType+"}数据源");
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
DataSourceConfig:
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import java.util.HashMap;
import java.util.Map;
/**
* author:ccf
*/
@Configuration
public class DataSourceConfig {
//master数据源
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
//slave数据源
@Bean(name = "slaveDatasource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDatasource() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
* @return
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
// 配置多数据源
Map
DynamicDataSource:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
private Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
// 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key
logger.info("数据源为"+DynamicDataSourceHolder.getDB());
return DynamicDataSourceHolder.getDB();
}
}
DataSourceEnum:这个枚举挺鸡肋的看个人喜好吧
public interface DataSourceEnum {
String MASTER_DATA_SOURCE_NAME = "masterDataSource";
String CLUSTER_DATA_SOURCE_NAME = "slaveDataSource";
}
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DataSourceAspect {
/**
* 在进入Service方法之前执行
*这里只是做了简单的关键字匹配,可按需求通过自定义注解、正则等方式处理
* @param point 切面对象
*/
@Before("execution(* xxx.xxx.xxx.service.*.*(..))")
public void before(JoinPoint point) {
// 获取到当前执行的方法名
String methodName = point.getSignature().getName();
// 设置数据源
if (isSlave(methodName)) {//从库
DynamicDataSourceHolder.setDB(DataSourceEnum.CLUSTER_DATA_SOURCE_NAME);
}else{//主库
DynamicDataSourceHolder.setDB(DataSourceEnum.MASTER_DATA_SOURCE_NAME);
}
}
@After("execution(* com.yili.report.service.*.*(..))")
public void afterSwitchDS(JoinPoint point){
DynamicDataSourceHolder.clearDB();
}
/**
* 判断是否为从库
* @param methodName
* @return
*/
private Boolean isSlave(String methodName) {
// 方法名以select,query、find、get开头的方法名走从库
return StringUtils.startsWithAny(methodName, "select","query", "find", "get");
}
}
注意!!!:主启动类过滤掉springboot的默认单数据源配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ReportApplication {
public static void main(String[] args) {
SpringApplication.run(ReportApplication.class, args);
}
}
以上就是关键的配置,其他的按照正常逻辑写controller、service、mapper即可