目录
3:在bootstrap.yml(application.properteis)或者application.yml中写入配置
4:定义DynamicDataSource类,用来动态连接数据源
5:定义DynamicDataSourceConfig,数据源的配置类
7:写一个AOP类DynamicDataSourceAspect,用来控制数据源切换
1:使用动态数据源的初衷
是能在应用层做到读写分离,即在程序代码中控制不同的查询方法去连接不同的库。除了这种方法以外,数据库中间件也是个不错的选择,它的优点是数据库集群对应用来说只暴露为单库,不需要切换数据源的代码逻辑。
思路:从动态数据源工具中获取到DataSource标示,根据标示获取DataSrouce对象。在Spring容器中,这一点其实更好实现。原理就是在DB访问之前,切换数据源。
***我们通过自定义注解 + AOP的方式实现数据源动态切换。
2:导入所需要的jar
<dependency> <!-- MySql驱动 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency> <!-- 连接池 -->
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
3:在bootstrap.yml(application.properteis)或者application.yml中写入配置
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
center:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/test1?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
duanguo:
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true
4:定义DynamicDataSource类,用来动态连接数据源
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* Created by xz_10086
* 2022/6/15 0015 18:35
* 动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public static final Logger log= LoggerFactory.getLogger(DynamicDataSource.class);
//用来装载需要使用的数据源的线程名(即即将调用的数据源线程名变量)
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
/**
* 配置DataSource, defaultTargetDataSource为主数据库
*/
public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> dataSources) {
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(dataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
log.warn("切换数据源为:{}",dataSource);
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
5:定义DynamicDataSourceConfig,数据源的配置类
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.spring.cloud.api.util.DynamicDataSource;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* Created by xz_10086
* 2022/6/15 0015 18:28
*/
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid.center") // application.yml中对应属性的前缀
public DataSource dataSource1() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid.duanguo") // application.yml中对应属性的前缀
public DataSource dataSource2() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> dataSources = new HashMap<>(2);
dataSources.put("centerDS", dataSource1());
dataSources.put("duanguoDS", dataSource2());
// 还有数据源,在dataSources中继续添加
System.out.println("DataSources:" + dataSources);
//参数1为默认数据源,参数2为多数据源集合
return new DynamicDataSource(dataSource1(), dataSources);
}
}
6:写一个用来切换数据源的注解类DS
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface DS {
//设置默认数据源
String value() default "centerDS";
}
7:写一个AOP类DynamicDataSourceAspect,用来控制数据源切换
import com.spring.cloud.api.annotation.DS;
import com.spring.cloud.api.util.DynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Created by xz_10086
* 2022/6/15 0015 19:04
*/
@Aspect
@Component
public class DynamicDataSourceAspect {
@Before("@annotation(com.spring.cloud.api.annotation.DS)")
public void beforeSwitchDS(JoinPoint point){
//获得当前访问的class
Class<?> className = point.getTarget().getClass();
//获得访问的方法名
String methodName = point.getSignature().getName();
//得到方法的参数的类型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
//默认数据源
String dataSource = "centerDS";
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(DS.class)) {
DS annotation = method.getAnnotation(DS.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DynamicDataSource.setDataSource(dataSource);
}
//接口调用结束,即清除本地数据源线程变量,使其换回到默认数据源
@After("@annotation(com.spring.cloud.api.annotation.DS)")
public void afterSwitchDS(JoinPoint point){
DynamicDataSource.clearDataSource();
}
}
8:关闭springboot自带的数据源配置,导入自定义的数据配置(可以不用,这里这是为了提前加载配置类)
import com.joolun.cloud.center.api.config.DataSourceConfig;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Import;
/**
* @author xz_10086
* @date 2022/05/11
* 通用模块
*/
@Import({DataSourceConfig.class}) //可以不用写,这里作用只是提前导入此配置
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) //关闭springboot自带的数据源配置
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
9:使用
import com.spring.cloud.api.annotation.DS;
import com.spring.cloud.api.util.DynamicDataSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created xz_10086
* 2022/6/17 0017 18:33
*/
@RestController
@RequestMapping("/hello")
public class HelloController {
@DS("duanguoDS") //不写,则使用默认数据源
@GetMapping("/test")
public String test(){
return DynamicDataSource.getDataSource();
}
@GetMapping("/test1")
public String test1(){
return DynamicDataSource.getDataSource();
}
}