导入jar包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置文件信息
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 主数据源
master-db:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8
username: root
password: 123456
# 从数据源
slave-db:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&characterEncoding=utf-8
username: root
password: 123456
max-active: 20 # 最大连接池数量
initial-size: 5 # 初始化建立物理连接个数
min-idle: 5 # 最小连接池数量
max-wait: 3000 # 获取连接时最大等待时间(毫秒)
mybatis:
mapper-locations: classpath:mapper/*.xml
数据源配置
package com.wuhj.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
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 org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 数据源配置
* @Author wuhj
* @Date 2021/4/2 11:18
*/
@Configuration
@Component
public class DynamicDataSourceConfig {
//读取application配置,构建master-db数据源
@Bean
@ConfigurationProperties("spring.datasource.druid.master-db")
public DataSource myMasterDataSource(){
return DruidDataSourceBuilder.create().build();
}
//读取application配置,构建slave-db数据源
@Bean
@ConfigurationProperties("spring.datasource.druid.slave-db")
public DataSource mySlaveDataSource(){
return DruidDataSourceBuilder.create().build();
}
//读取application配置,创建动态数据源
@Bean
@Primary
public DynamicDataSource dynamicDataSource(DataSource myMasterDataSource, DataSource mySlaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master-db", myMasterDataSource);
targetDataSources.put("slave-db", mySlaveDataSource);
// myTestDbDataSource 默认数据源
// targetDataSources 目标数据源(多个)
return new DynamicDataSource(myMasterDataSource, targetDataSources);
}
}
数据源动态切换
package com.wuhj.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* @Description: 数据源动态切换
* @Author wuhj
* @Date 2021/4/2 11:06
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
// 通过ThreadLocal维护一个全局唯一的map来实现数据源的动态切换
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
//指定使用哪一个数据源
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
自定义注解MyDataSource,结合AOP实现数据源动态切换
package com.wuhj.annotation;
import java.lang.annotation.*;
/**
* @Description: 自定义数据源注解
* @Author wuhj
* @Date 2021/4/2 11:23
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyDataSource {
String name() default "";
}
DataSourceAspect切面类,以MyDataSource注解为切入点。在执行被@MyDataSource注解的方法时,获取该注解传入的name,并切换到指定数据源执行,执行完成后切换回默认数据源
package com.wuhj.aspect;
import com.wuhj.config.DynamicDataSource;
import com.wuhj.annotation.MyDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @Description: 数据源切面
* @Author wuhj
* @Date 2021/4/2 11:27
*/
@Aspect
@Component
@Slf4j
public class DataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
//只有调用@MyDataSource注解的方法才会触发around
@Pointcut("@annotation(com.wuhj.annotation.MyDataSource)")
public void dataSourcePointCut() {
}
//截取使用MyDataSource注解的方法,切换指定数据源
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
logger.info("execute DataSourceAspect around=========>"+method.getName());
// 1. 获取自定义注解MyDataSource,查看是否配置指定数据源名称
MyDataSource dataSource = method.getAnnotation(MyDataSource.class);
if(dataSource == null){
logger.info("默认数据源");
// 1.1 使用默认数据源
DynamicDataSource.setDataSource("master-db");
}else {
// 1.2 使用指定名称数据源
DynamicDataSource.setDataSource(dataSource.name());
logger.info("使用指定名称数据源=========>"+dataSource.name());
}
try {
return point.proceed();
} finally {
// 后置处理 - 恢复默认数据源
logger.info("恢复数据源");
DynamicDataSource.clearDataSource();
}
}
}
配置启动类
package com.wuhj;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* @Description:
* @Author wuhj
* @Date 2021/4/2 11:06
*/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//不加载默认数据源配置
@MapperScan(basePackages = {"com.wuhj.mapper"})
public class MultipleDataSourceApplication {
public static void main(String[] args) {
SpringApplication.run(MultipleDataSourceApplication.class, args);
}
}
User对象
package com.wuhj.bean;
import lombok.Data;
/**
* @Description:
* @Author wuhj
* @Date 2021/4/2 11:41
*/
@Data
public class User {
private long id;
private String name;
private int age;
}
Controller层
package com.wuhj.controller;
import com.wuhj.bean.User;
import com.wuhj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @Description:
* @Author wuhj
* @Date 2021/4/2 11:38
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/master")
public List<User> testMaster(){
List<User> userList = userService.findMasterAll();
return userList;
}
@GetMapping("/slave")
public List<User> testSlave(){
List<User> userList = userService.findSlaveAll();
return userList;
}
}
Service层
package com.wuhj.service;
import com.wuhj.annotation.MyDataSource;
import com.wuhj.bean.User;
import com.wuhj.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Description:
* @Author wuhj
* @Date 2021/4/2 11:38
*/
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
//使用默认数据源
public List<User> findMasterAll() {
return userMapper.findUserAll();
}
//使用@MyDataSource注解切换数据源
@MyDataSource(name = "slave-db")
public List<User> findSlaveAll() {
return userMapper.findUserAll();
}
}
Mapper层
package com.wuhj.mapper;
import com.wuhj.bean.User;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Description:
* @Author wuhj
* @Date 2021/4/2 11:35
*/
@Repository
public interface UserMapper {
List<User> findUserAll();
}
UserMapper.xml文件信息
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wuhj.mapper.UserMapper">
<!--查询user信息-->
<select id="findUserAll" resultType="com.wuhj.bean.User">
select * from user;
</select>
</mapper>
数据库信息,创建数据库test和test2,分别执行脚本建表并插入测试数据
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 80023
Source Host : localhost:3306
Source Database : test
Target Server Type : MYSQL
Target Server Version : 80023
File Encoding : 65001
Date: 2021-04-07 10:07:18
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint NOT NULL,
`name` varchar(32) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '14');
INSERT INTO `user` VALUES ('2', 'lisi', '13');
INSERT INTO `user` VALUES ('3', 'wangwu', '16');
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 80023
Source Host : localhost:3306
Source Database : test2
Target Server Type : MYSQL
Target Server Version : 80023
File Encoding : 65001
Date: 2021-04-07 10:08:22
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint NOT NULL,
`name` varchar(32) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('4', 'sunliu', '9');
INSERT INTO `user` VALUES ('5', 'gouqi', '15');
INSERT INTO `user` VALUES ('6', 'maoba', '13');
测试结果
接口地址:http://localhost:9999/user/master
返回结果:[{"id":1,"name":"zhangsan","age":14},{"id":2,"name":"lisi","age":13},{"id":3,"name":"wangwu","age":16}]
接口地址:http://localhost:9999/user/slave
返回结果:[{"id":4,"name":"sunliu","age":9},{"id":5,"name":"gouqi","age":15},{"id":6,"name":"maoba","age":13}]
结论:通过自定义注解和AOP实现了数据源动态切换