基础配置
application.yml
spring:
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
DataSourceConfig
package com.example.multids.config;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.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource; import java.util.HashMap; import java.util.Map;@Configuration public class DataSourceConfig {// 主数据源配置(前缀对应application.yml中的spring.datasource.master) @Bean@ConfigurationProperties(prefix = "spring.datasource.master")public DataSource masterDataSource() {return DataSourceBuilder.create().build();}// 从数据源配置 @Bean@ConfigurationProperties(prefix = "spring.datasource.slave")public DataSource slaveDataSource() {return DataSourceBuilder.create().build();}// 动态数据源路由 @Beanpublic DataSource routingDataSource(DataSource masterDataSource, DataSource slaveDataSource) {AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSourceType();}};Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("master", masterDataSource);targetDataSources.put("slave", slaveDataSource);routingDataSource.setDefaultTargetDataSource(masterDataSource);routingDataSource.setTargetDataSources(targetDataSources);return routingDataSource;} }
DataSourceContextHolder
package com.example.multids.config;public class DataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();// 设置当前线程数据源类型public static void setDataSourceType(String dsType) {contextHolder.set(dsType);}// 获取当前线程数据源类型public static String getDataSourceType() {return contextHolder.get();}// 清除数据源类型public static void clearDataSourceType() {contextHolder.remove();} }
Master
package com.example.multids.annotation;import java.lang.annotation.*;@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Master {// 标记使用主数据源 }
DataSourceAspect
package com.example.multids.aspect;import com.example.multids.annotation.Master; import com.example.multids.config.DataSourceContextHolder; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component;@Aspect @Component @Order(-1) // 保证在事务注解前执行 public class DataSourceAspect {@Around("@annotation(master)")public Object around(ProceedingJoinPoint point, Master master) throws Throwable {try {DataSourceContextHolder.setDataSourceType("master");return point.proceed();} finally {DataSourceContextHolder.clearDataSourceType();}} }
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies> </project>