这两天的学习得知,springboot配置多数据源可以分为两种方式,一种是分包的形式,一种是基于aop的形式。各有优缺点,看自己了。今天简单来实现一下基于aop的形式。
首先是我的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MySql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
接下来是我的数据库配置信息
spring:
#数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://127.0.0.1:3306/ljw?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&allowMultiQueries=true
username: root
password: root
# 从库数据源
slave:
url: jdbc:mysql://175.24.234.186:3306/ljw?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&allowMultiQueries=true
username: root
password: 123456
要实现多数据源,那么我们可以利用spring提供的预留接口去处理->AbstractRoutingDataSource
贴上我们的数据源配置类
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
/**
* 数据源配置类
* @author ljw
*
*/
@Configuration
public class DruidConfig{
/**
* 主数据源
* @return
*/
@Bean(name="masterDataSource")
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
/**
* 从数据源
* @return
*/
@Bean(name="slaveDataSource")
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource slaveDataSource(){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
/**
* 动态数据源配置
* @param masterDataSource
* @param slaveDataSource
* @return
*/
@Bean(name = "dynamicDataSource")
@Primary
public DataSource dynamicDataSource (@Qualifier(value="masterDataSource") DataSource masterDataSource,@Qualifier(value="slaveDataSource") DataSource slaveDataSource){
//设置默认数据源
DynamicDataSource dd = new DynamicDataSource();
dd.setDefaultTargetDataSource(masterDataSource);
//多数据源配置
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource);
targetDataSources.put("slave", slaveDataSource);
dd.setTargetDataSources(targetDataSources);
return dd;
}
}
这里动态去获取数据源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
System.err.println("数据源为:"+DataSourceContextSupport.get());
return DataSourceContextSupport.get();
}
}
为了保证线程安全,我们使用ThreadLocal去存储数据源的key
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* @author ljw
*/
@Component
public class DataSourceContextSupport {
public static final ThreadLocal<String> dataContext = new ThreadLocal<>();
private static final String dataType = "master";//默认走主库
/**
* 设置当前信息
*/
public static void set(String type) {
dataContext.set(type);
}
/**
* 获取当前数据信息
*/
public static String get() {
return Optional.ofNullable(dataContext.get()).orElse(dataType);
}
/**
* 移除当前数据信息
*/
public static void remove() {
dataContext.remove();
}
}
由于我们是自定义数据源,所以我们在启动的时候要移除默认的
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MpApplication {
public static void main(String[] args) {
SpringApplication.run(MpApplication.class, args);
}
}
此时我们启动项目来看一下效果,默认走的是主数据源,和我们的配置对应
那么如何实现切换数据源呢?
要实现切换数据源,我们可以先自定义一个注解DataSource 默认主数据源
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 指定数据源注解
* @author ljw
*
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource{
/**
* 默认主数据源
*/
public String value() default "master";
}
在执行前设置当前数据源
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 多数据源切面管理
* @author ljw
*
*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect{
//切入点
@Pointcut("@annotation(com.spring.framework.config.DataSource)")
public void pointCut(){}
@Before("pointCut()")
public void doBefore(JoinPoint point) throws Throwable{
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null){
if(method.getAnnotation(DataSource.class)!=null) {
DataSource datasource = method.getAnnotation(DataSource.class);
String value = datasource.value();
DataSourceContextSupport.set(value);
}
}
}
@After("pointCut()")
public void doAfter(JoinPoint point) throws Throwable{
DataSourceContextSupport.remove();
}
}
看一下效果
简单的配置到这里就结束了,至于事务问题在这先不做讨论。小伙伴可以简单了解一下多数据源如何配置即可。