springboot讀寫分離

前言:本文采用springboot集成AOP的方式實現讀寫分離

1.準備工作,mybaties主從數據庫的搭建,不會的同學可以瀏覽這篇博客https://blog.csdn.net/qq_30374237/article/details/106624263

2.引入依賴

 <!--導入AOP依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 數據庫連接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>

3.添加主從庫的配置

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.url=jdbc:mysql://192.168.1.156:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.datasource.master.username=root
spring.datasource.master.password=****
spring.datasource.slave.url=jdbc:mysql://192.168.1.154:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.datasource.slave.username=root
spring.datasource.slave.password=****

4.定義線程類,用於從庫的設置,清空和獲取

public class DataSourceThread {
    private static final ThreadLocal<String> DATASOURCE_THREAD = new ThreadLocal<>();

    public static void set() {
        DATASOURCE_THREAD.set("slave");
    }

    public static String get() {
        return DATASOURCE_THREAD.get();
    }

    public static void clear() {
        DATASOURCE_THREAD.remove();
    }

5.由於多個數據源,數據庫配置需要手動配置

@Configuration
@MapperScan("com.bjiang.datasource.read.write.dao")
public class ReadOrWriteDaraSource {
    /**
     * 主庫配置注入
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource master() {
        //new DruidDataSource();
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 從庫配置注入
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slave() {
        //new DruidDataSource();
        return DruidDataSourceBuilder.create().build();
    }
    /**
     * 主從動態配置
     */
    @Bean
    public SlaveDataSource dynamic(@Qualifier("master") DataSource masterDataSource,
                                       @Autowired(required = false) @Qualifier("slave") DataSource slaveDataSource) {
        SlaveDataSource dynamicDataSource = new SlaveDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource);
        if (slaveDataSource != null) {
            targetDataSources.put("slave", slaveDataSource);
        }
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        return dynamicDataSource;
    }
/*
* 多個數據源需要配置SqlSessionFactory
* */
    @Bean
    public SqlSessionFactory sessionFactory(@Qualifier("dynamic") DataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setTypeAliasesPackage("com.bjiang.datasource.read.write.dao");
        bean.setDataSource(dynamicDataSource);
        return bean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlTemplate(@Qualifier("sessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean(name = "dataSource")
    public DataSourceTransactionManager dataSource(@Qualifier("dynamic") DataSource dynamicDataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dynamicDataSource);
        return dataSourceTransactionManager;
    }

6.重寫方法,設置以及獲取DataSource

public class SlaveDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceThread.get();
    }
}

7.自定義註解實現AOP,用於註解需要使用從庫的方法

/**
 * 自定義註解實現AOP,用於註解需要使用從庫的方法
 * */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Slave {
}

8.編寫AOP類進行攔截,實現切換數據源

@Aspect
@Order(value = 1)
@Component
public class DataSourceAop {
    @Around("@annotation(com.bjiang.datasource.read.write.aop.Slave)")
    public Object setDynamicDataSource(ProceedingJoinPoint pjp) throws Throwable {
        boolean clear = true;
        try {
            DataSourceThread.set();
            log.info("切換數據源");
            return pjp.proceed();
        } finally {
            if (clear) {
                DataSourceThread.clear();
            }
        }
    }
}

9.編寫測試方法,需要使用從庫時加上註解@Slave

10.測試

顯示主庫數據已插入

顯示查看數據時已切換數據源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章