Spring Boot + MyBatis-Plus 读写分离与多 Slave 负载均衡示例
一、项目结构
src/main/java/com/example/demo/
├── config/
│ ├── DataSourceConfig.java # 数据源配置
│ ├── MyBatisPlusConfig.java # MyBatis-Plus配置
├── constant/
│ ├── DataSourceType.java # 数据源类型枚举
├── context/
│ ├── DataSourceContextHolder.java # 数据源上下文
├── aspect/
│ ├── DataSourceAspect.java # 注解驱动的数据源切换
│ ├── DataSourceRouteAspect.java # 自动路由的数据源切换
├── annotation/
│ ├── DataSource.java # 自定义数据源注解
├── loadbalancer/
│ ├── SlaveDataSourceLoadBalancer.java # 从库负载均衡器
├── mapper/
│ ├── UserMapper.java # DAO接口
├── service/
│ ├── UserService.java # 服务接口
│ ├── impl/
│ └── UserServiceImpl.java # 服务实现
├── controller/
│ ├── UserController.java # 控制器
└── DemoApplication.java # 启动类
二、核心代码实现
(一)数据源类型枚举
package com.example.demo.constant;
public enum DataSourceType {
MASTER,
SLAVE_1,
SLAVE_2,
SLAVE_3
}
(二)数据源上下文
package com.example.demo.context;
import com.example.demo.constant.DataSourceType;
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>();
public static void setDataSource(DataSourceType type) {
contextHolder.set(type);
}
public static DataSourceType getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
(三)动态数据源
package com.example.demo.config;
import com.example.demo.context.DataSourceContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
(四)数据源配置
package com.example.demo.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.example.demo.constant.DataSourceType;
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;
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "slave1DataSource")
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "slave2DataSource")
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "slave3DataSource")
@ConfigurationProperties("spring.datasource.slave3")
public DataSource slave3DataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, masterDataSource());
targetDataSources.put(