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<String> holder = new ThreadLocal<String>(); /** * 绑定当前线程数据源 * * @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<Object, Object> 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<TbUser> selectByExample(TbUserExample example);
11 测试结果
总结:通过AOP来确定所使用数据源类型,然后通过路由来进行数据源选择。