springboot 動態數據源 以及 使用註解切換數據源

使用springboot項目時用到了多數據源配置,在網上看了很多總感覺達不到自己的要求,所以瀏覽完之後決定自己寫一個。

廢話不說,上代碼

pom.xml需要用到的支持

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example.boot</groupId>
    <artifactId>proliu</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>proliu</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.9</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>

        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>

</project>

 我比較懶就把所有的全都粘過來了,但是這是一個我用來測試多數據源的項目,多餘的支持包不多。

再來看application.yml


#使用多數據源配置,自定義配置我訂的規則,就是在項目中讓程序讀取到的配置的key值
spring:
  #list 數據源名字的集合  名字自己訂只需要在每個數據源上一級以該名字定義   第一個爲默認數據源
  list: master,save
  datasource:
    #數據源配置基本配置  其他的統一配置可以自己加 我比較懶  注:該級別的名字應該與上邊list一一對應,不然會怎麼樣我也不知道 嘿嘿
    master:
      driverClassCame: com.mysql.jdbc.Driver
      url: ://*************:3306/*************?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
      username: root
      password: 
    save:
      driverClassCame: com.mysql.jdbc.Driver
      url: jdbc:mysql://*************:3306/*************?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
      username: root
      password: 
#使用mybatis 需要的兩個配置信息
mybatis:
  mapper-locations:  classpath*:com/example/boot/proliu/map/*.xml
  type-aliases-package: com.example.boot.proliu.entity

 

使用自定義多數據源配置  首先就是再啓動類去掉項目自帶的自動加載數據源代碼

//exclude = DataSourceAutoConfiguration.class 去掉自動加載數據源 不然會造成循環依賴
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = "com.example.boot.proliu.map")//mapper掃描
public class ProliuApplication {

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

}

使用多數據源需要自己定義配置文件信息獲取和數據源配置 進入主題

package com.example.boot.proliu.config;

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

import javax.sql.DataSource;
import java.util.Map;

/**
 * 繼承AbstractRoutingDataSource重寫determineCurrentLookupKey
 * 自己定義需要那個數據源
 */
public class MyDynamicDataSource extends AbstractRoutingDataSource {


    //線程獨有數據源切換    使用 ThreadLocal
     private final static   ThreadLocal<String > databasekey = new ThreadLocal<>();


    /**
     * 配置DataSource, defaultTargetDataSource爲主數據庫  TargetDataSources爲數據源集合
     */
    public MyDynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    public static void setDatabasekey(String key) {
        databasekey.set(key);
    }

    public static String getDatabasekey() {
       return databasekey.get();
    }

    public static void removeDatabasekey() {
         databasekey.remove();
    }


    @Override
    protected Object determineCurrentLookupKey() {
        return databasekey.get();
    }
}

簡單解釋一下 AbstractRoutingDataSource, 該類充當了DataSource的路由中介, 能有在運行時, 根據某種key值來動態切換到真正的DataSource上。

有了數據源切換類了,下邊貼一下數據源加載類

package com.example.boot.proliu.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



@Configuration
public class MyDataSourse {


    @Autowired
    Environment env;






    /**
     * 將獲取的數據源配置註冊到MyDynamicDataSource  或者說 AbstractRoutingDataSource 中
     * @return
     */
    @Bean
    @Primary
    public MyDynamicDataSource dataSource() {
        Map<Object, Object> targetDataSources = getdate();
        return new MyDynamicDataSource((DataSource)targetDataSources.get("default"), targetDataSources);
    }


    /**
     * 加載數據源時需要讀取數據配配置到DruidDataSource類裏 我這裏用阿里的驅動
     * 默認數據源我這使用default做爲key
     * 這裏用到了Environment來讀取配置
     * 我和看的資料裏就這有分歧  我認爲這樣更加簡單清晰
     * @return
     */
    private Map<Object,Object> getdate(){

        List<String> listTest = env.getProperty("spring.list", List.class);

        Map<Object,Object> dataSourceMap = new HashMap<>();

        for (String s : listTest) {

            DruidDataSource oneDateSource = new DruidDataSource();
            oneDateSource.setDriverClassName(env.getProperty("spring.datasource."+s+".driverClassCame"));
            oneDateSource.setUrl(env.getProperty("spring.datasource."+s+".url"));
            oneDateSource.setUsername(env.getProperty("spring.datasource."+s+".username"));
            oneDateSource.setPassword(env.getProperty("spring.datasource."+s+".password"));
            dataSourceMap.put(s,oneDateSource);


        }

        dataSourceMap.put("default",dataSourceMap.get(listTest.get(0)));

        return dataSourceMap;

    }

}

到這基本的數據源配置就完成了。只需要在需切換的數據源的地方

MyDynamicDataSource.setDatabasekey(key); //參數爲配置中list中的數據源名稱

注意   使用這種方式調用完後應該釋放數據源回覆到默認狀態  

MyDynamicDataSource.removeDatabasekey();

但是這麼搞對代碼侵入性太強  因此使用aop形式對服務方法進行增強,在增強中進行數據源切換

 

首先  定義註解  上代碼

package com.example.boot.proliu.aspect;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicDateSource {

    String value() default "";

}

這裏不多解釋,不懂的可以看看java註解這一塊

接下來 就是增強類   代碼

package com.example.boot.proliu.aspect;

import com.example.boot.proliu.config.MyDynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


/**
 * 裏邊用到的幾個註解
 *
 * @Aspect
 * @Pointcut
 * @Before
 * @After  這都是aop這一塊的註解
 */
@Aspect
@Component
public class DataSpurceAspect {


    //定義切面
    @Pointcut("@annotation(com.example.boot.proliu.aspect.DynamicDateSource)")
    public void annotationPointCut() {

    }

    //前置增強 檢查方法是否需要切換數據源 和數據源切換
    @Before("annotationPointCut()")
    public void before(JoinPoint joinPoint){
        MethodSignature sign =  (MethodSignature)joinPoint.getSignature();
        Method method = sign.getMethod();
        DynamicDateSource annotation = method.getAnnotation(DynamicDateSource.class);
        if(annotation != null){

            MyDynamicDataSource.setDatabasekey(annotation.value());
        }
    }

    //後置增強  方法調用完之後回覆默認數據源   @After註解是不管怎樣都會執行
    @After("annotationPointCut()")
    public void after(){
        if(null != MyDynamicDataSource.getDatabasekey())
            MyDynamicDataSource.removeDatabasekey();
    }
}

 

到配置基本完成   

只需要在需要的切換的方法上加上

@DynamicDateSource(“數據源的KEY”)註解就可以了

附上項目地址

springboot 動態數據源配置  

項目地址   https://github.com/ljhaa/dynamicDataSource.git

 

 

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