SpringBoot AOP Mysql主从复制
1.原理
借助spring的【org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource】这个抽象类实现,来进行·数据源的路由,并通过Aop 进行路由选择。
2 配置主从数据源
###datasource
spring.datasource.master.driverClassName = com.mysql.jdbc.Driver
#多数据源时,为master
spring.datasource.master.url = jdbc:mysql://192.168.18.131:3339/usermanage
spring.datasource.master.username = root
spring.datasource.master.password = 123456
spring.datasource.slave.driverClassName= com.mysql.jdbc.Driver
多数据源时,为slave
spring.datasource.slave.url = jdbc:mysql://192.168.18.131:3340/usermanage
spring.datasource.slave.username =root
spring.datasource.slave.password =123456
3定义HandleDataSource类来获取当前线程的数据源类型
/**
* Created by huanghengbo on 2019/8/21.
*/
public class HandleDataSource {
public static final ThreadLocal holder = new ThreadLocal();
/**
* 绑定当前线程数据源
*
* @param key
*/
public static void putDataSource(String datasource)
{
holder.set(datasource);
}
/**
* 获取当前线程的数据源
*
* @return
*/
public static String getDataSource()
{
return holder.get();
}
}
4定义路由数据源的实现类MyAbstractRoutingDataSource
/**
* Created by huanghengbo on 2019/8/20.
*/
public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource{
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
protected Object determineCurrentLookupKey() {
log.info(HandleDataSource.getDataSource());
return HandleDataSource.getDataSource();//获取对应的数据源
}
}
5新建配置类配置数据源数据源和路由配置
@Configuration
public class DataSourceConfig {
//主数据源
@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();
}
/**
* 设置数据源路由,通过该类中的determineCurrentLookupKey决定使用哪个数据源
*/
@Bean
public AbstractRoutingDataSource routingDataSource() {
MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource();
Map targetDataSources = new HashMap<>(2);//存放对于数据源的映射
targetDataSources.put("master", MasterDataSource());
targetDataSources.put("slave", SlaveDataSource());
proxy.setDefaultTargetDataSource(MasterDataSource());
proxy.setTargetDataSources(targetDataSources);
return proxy;
}
@Bean(name = "SqlSessionFactory")
@Primary
public SqlSessionFactory MasterSqlSessionFactory( DataSource routingDataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(routingDataSource);//DataSource使用路由数据源
//���XMLĿ¼
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
bean.setMapperLocations(resolver.getResources("classpath*:mappers/*.xml"));
bean.setConfigLocation(resolver.getResource("classpath:mybatis-config.xml"));
return bean.getObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Bean(name = "TransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(DataSource routingDataSource) {
return new DataSourceTransactionManager(routingDataSource);
}
@Bean(name = "SqlSessionTemplate")
@Primary
public SqlSessionTemplate MasterSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
6新建DataSource注解来注释Mapper接口所要使用的数据源
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource
{
String value();//设置数据源类型
}
7配置Aop
@Aspect
@Component
public class DataSourceAspect {
/**
* 在dao层方法获取datasource对象之前,在切面中指定当前线程数据源
*/
@Pointcut("execution(* com.spring.dao*..*(..))")//切点为所有的mapper接口
public void pointcut(){
}
@Before("pointcut()")
public void before(JoinPoint point)
{
System.out.println("before");
Object target = point.getTarget();
String method = point.getSignature().getName();
Class>[] classz = target.getClass().getInterfaces(); // 获取目标类的接口, 所以@DataSource需要写在接口上
Class>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try
{
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class))
{
DataSource data = m.getAnnotation(DataSource.class);
System.out.println("用户选择数据库库类型:" + data.value());
HandleDataSource.putDataSource(data.value()); // 数据源放到当前线程中
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}
10 在对应的mapper方法上使用注解DataSource("")来设置数据源类型
@DataSource("slave")
List selectByExample(TbUserExample example);
11 测试结果
总结:通过AOP来确定所使用数据源类型,然后通过路由来进行数据源选择。