二. Mysql读写分离 :springboot服务端AbstractRoutingDataSource整合

一 DataSourceConfig.class

/**
 * 数据源配置类
 */
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

    /**
     * 只写数据源
     *
     * @return
     */
    @Bean // 声明bean
    @ConfigurationProperties("spring.datasource.druid.write") // 获取配置文件属性
    @Primary // 防止循环依赖
    public DataSource writeDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 只读数据源
     *
     * @return
     */
    @Bean
    @ConfigurationProperties("spring.datasource.druid.read")
    public DataSource readDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 动态数据源
     *
     * @param writeDataSource
     * @param readDataSource
     * @return
     */
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dataSource(@Qualifier("writeDataSource") DataSource writeDataSource, @Qualifier("readDataSource") DataSource readDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.WRITE.name(), writeDataSource);
        targetDataSources.put(DataSourceType.READ.name(), readDataSource);
        return new DynamicDataSource(writeDataSource, targetDataSources);
    }

    /**
     * 根据数据源创建SqlSessionFactory
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource,
                                               @Value("${mybatis.typeAliasesPackage}") String typeAliasesPackage,
                                               @Value("${mybatis.mapperLocations}") String mapperLocations) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        // 指定数据源(这个必须有,否则报错)
        factoryBean.setDataSource(dynamicDataSource);
        // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
        factoryBean.setTypeAliasesPackage(typeAliasesPackage);// 指定基包
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        return factoryBean.getObject();
    }

    /**
     * 配置事务管理器
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
    }
}

二.DataSourceType
/**
 * 数据源枚举类
 */
public enum DataSourceType {
    /**
     * 只写库
     */
    WRITE,

    /**
     * 只读库
     */
    READ
}

三DynamicDataSource.class
/**
 * 动态数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDateSoureType();
    }

}

四.DynamicDataSourceContextHolder.class

/**
 * 数据源切换处理
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDateSoureType(String dsType)
    {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDateSoureType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDateSoureType()
    {
        CONTEXT_HOLDER.remove();
    }

}
五.DataSourceAspect.class
/**
 * 针对带有数据源类型注解方法的切面
 */
@Aspect
@Component
@Order(1)
public class DataSourceAspect {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.vesus.springbootfastdfs.annotation.DataSourceTypeAnnotation)")
    public void pointCut() {
    }


    /**
     * 环绕通知
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("pointCut()")
    public Object aspectPointBefor(ProceedingJoinPoint point) throws Throwable {
        // 获取该切点(带该注解)的方法
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        // 获取真正调用的方法(serviceImpl)/**此处容易遗漏出错*/
        Method relmethod = point.getTarget().getClass()
                .getMethod(point.getSignature().getName(), method.getParameterTypes());
        // 获取该方法(serviceImpl)上的注解
        DataSourceTypeAnnotation annotation = relmethod.getAnnotation(DataSourceTypeAnnotation.class);
        if (annotation != null) {
            // 设置该方法的数据源类型
            logger.info("数据源{}注解值", annotation.value());
            DynamicDataSourceContextHolder.setDateSoureType(annotation.value());
        }
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDateSoureType();
        }
    }
}

六.application.yml(部分)

备注:以上是该方法核心代码,自定义注解需要自己编写;

猜你喜欢

转载自blog.csdn.net/weixin_39494923/article/details/84560758