1、配置两个数据源信息
package com.site.blog.my.core.config;
import org.springframework.beans.factory.annotation.Qualifier;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》
* 79. Data Access
* 79.1 Configure a Custom DataSource
* 79.2 Configure Two DataSources
*/
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
/* @Bean
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DataSourceBuilder.create().build();
}*/
@Bean
public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slave1DataSource") DataSource slave1DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);
//targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSources);
return myRoutingDataSource;
}
}
2、配置切换数据源工具类
package com.site.blog.my.core.config;
import java.util.concurrent.atomic.AtomicInteger;
public class DBContextHolder {
private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
private static final AtomicInteger counter = new AtomicInteger(-1);
public static void set(DBTypeEnum dbType) {
contextHolder.set(dbType);
}
public static DBTypeEnum get() {
return contextHolder.get();
}
public static void master() {
set(DBTypeEnum.MASTER);
System.out.println("切换到master");
}
public static void slave() {
// 轮询
int index = counter.getAndIncrement() % 2;
if (counter.get() > 9999) {
counter.set(-1);
}
set(DBTypeEnum.SLAVE1);
System.out.println("切换到slave1");
/* if (index == 0) {
set(DBTypeEnum.SLAVE1);
System.out.println("切换到slave1");
}else {
set(DBTypeEnum.SLAVE2);
System.out.println("切换到slave2");
}*/
}
}
3、配置Aop切面,对不同方法进行过滤,选择不同数据源
package com.site.blog.my.core.config;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Order(-1)
@Component
public class DataSourceAop {
@Pointcut("!@annotation(com.site.blog.my.core.annotation.Master) " +
"&& (execution(* com.site.blog.my.core.service..*.select*(..)) " +
"|| execution(* com.site.blog.my.core.service..*.get*(..)))" +
"|| execution(* com.site.blog.my.core.service..*.login*(..)))")
public void readPointcut() {
}
@Pointcut("@annotation(com.site.blog.my.core.annotation.Master) " +
"|| execution(* com.site.blog.my.core.service..*.insert*(..)) " +
"|| execution(* com.site.blog.my.core.service..*.add*(..)) " +
"|| execution(* com.site.blog.my.core.service..*.update*(..)) " +
"|| execution(* com.site.blog.my.core.service..*.edit*(..)) " +
"|| execution(* com.site.blog.my.core.service..*.delete*(..)) " +
"|| execution(* com.site.blog.my.core.service..*.remove*(..))" +
"|| execution(* com.site.blog.my.core.service..*.save*(..))")
public void writePointcut() {
}
@Before("readPointcut()")
public void read() {
DBContextHolder.slave();
}
@Before("writePointcut()")
public void write() {
DBContextHolder.master();
}
/**
* 另一种写法:if...else... 判断哪些需要读从数据库,其余的走主数据库
*/
// @Before("execution(* com.site.blog.my.core.service.impl.*.*(..))")
// public void before(JoinPoint jp) {
// String methodName = jp.getSignature().getName();
//
// if (StringUtils.startsWithAny(methodName, "get", "select", "find")) {
// DBContextHolder.slave();
// }else {
// DBContextHolder.master();
// }
// }
}
4、附录数据源配置信息
# read and write datasource
spring.datasource.master.jdbc-url=jdbc:mysql://xxxxx:3306/huxblog?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=GMT
spring.datasource.master.username=xxxxx
spring.datasource.master.password=xxxxx
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave1.jdbc-url=jdbc:mysql://xxxxx:3306/huxblog?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=GMT
spring.datasource.slave1.username=xxxxx
spring.datasource.slave1.password=xxxxx
spring.datasource.slave1.driver-class-name=com.mysql.cj.jdbc.Driver
5、验证读写分离完成,结合数据库的同步使用,即可完成数据同步以及读写分离功能。 读写分离还可以借助其他工具包实现,如mycat。