springboot+AOP+MySQL+mybatis多数据源动态切换实现MySQL读写分离

开发场景中经常会遇到多数据源的情况,比如MySQL读写分离等,下面是通过springboot+AOP实现.需要的maven依赖

springboot+AOP+MySQL+mybatis多数据源动态切换实现MySQL读写分离_第1张图片


    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-aop


    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    1.3.0



    com.alibaba
    druid
    1.0.31



    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    1.3.0



    mysql
    mysql-connector-java


    com.github.noraui
    ojdbc7
    12.1.0.2

yml配置:

server:
    port: 8080
spring:
   datasource:
       master:
           url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
           username: root
           password: 123456
           driver-class-name: com.mysql.jdbc.Driver
           max-idle: 10
           max-wait: 10000
           min-idle: 5
           initial-size: 5
       slave:
           url: jdbc:mysql://localhost:3307/test
           username: root
           password: 123456
           driver-class-name: com.mysql.jdbc.Driver
           max-idle: 10
           max-wait: 10000
           min-idle: 5
           initial-size: 5

关键的配置文件目录:

springboot+AOP+MySQL+mybatis多数据源动态切换实现MySQL读写分离_第2张图片

DynamicDataSourceHolder:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DynamicDataSourceHolder {
    private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceHolder.class);
    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    // 设置数据源名
    public static void setDB(String dbType) {
        logger.info("切换到{"+dbType+"}数据源");
        contextHolder.set(dbType);
    }

    // 获取数据源名
    public static String getDB() {
        return (contextHolder.get());
    }

    // 清除数据源名
    public static void clearDB() {
        contextHolder.remove();
    }
}

DataSourceConfig:

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
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 org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import java.util.HashMap;
import java.util.Map;


/**
 * author:ccf
 */
@Configuration
public class DataSourceConfig {
    //master数据源
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    //slave数据源
    @Bean(name = "slaveDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDatasource() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     * @return
     */
    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        // 配置多数据源
        Map dsMap = new HashMap();
        dsMap.put("masterDataSource", masterDataSource());
        dsMap.put("slaveDataSource", slaveDatasource());
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }

    /**
     * 配置@Transactional注解事物
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }

}

DynamicDataSource:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    private Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
        // 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key
        logger.info("数据源为"+DynamicDataSourceHolder.getDB());
        return DynamicDataSourceHolder.getDB();
    }
}

DataSourceEnum:这个枚举挺鸡肋的看个人喜好吧

public interface DataSourceEnum {
    String MASTER_DATA_SOURCE_NAME = "masterDataSource";
    String CLUSTER_DATA_SOURCE_NAME = "slaveDataSource";

}
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DataSourceAspect {

    /**
     * 在进入Service方法之前执行
     *这里只是做了简单的关键字匹配,可按需求通过自定义注解、正则等方式处理
     * @param point 切面对象
     */
    @Before("execution(* xxx.xxx.xxx.service.*.*(..))")
    public void before(JoinPoint point) {
        // 获取到当前执行的方法名
        String methodName = point.getSignature().getName();
        // 设置数据源
        if (isSlave(methodName)) {//从库
            DynamicDataSourceHolder.setDB(DataSourceEnum.CLUSTER_DATA_SOURCE_NAME);
        }else{//主库
            DynamicDataSourceHolder.setDB(DataSourceEnum.MASTER_DATA_SOURCE_NAME);
        }
    }

    @After("execution(* com.yili.report.service.*.*(..))")
    public void afterSwitchDS(JoinPoint point){
        DynamicDataSourceHolder.clearDB();
    }

    /**
     * 判断是否为从库
     * @param methodName
     * @return
     */
    private Boolean isSlave(String methodName) {
        // 方法名以select,query、find、get开头的方法名走从库
        return StringUtils.startsWithAny(methodName, "select","query", "find", "get");
    }
}

注意!!!:主启动类过滤掉springboot的默认单数据源配置

​​​​@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ReportApplication {

	public static void main(String[] args) {
		SpringApplication.run(ReportApplication.class, args);
	}

}

 

以上就是关键的配置,其他的按照正常逻辑写controller、service、mapper即可

 

 

你可能感兴趣的:(后端)