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 测试结果
SpringBoot AOP Mysql主从复制_SpringBoot总结:通过AOP来确定所使用数据源类型,然后通过路由来进行数据源选择。