SpringBoot——?jiǎng)討B(tài)數(shù)據(jù)源(多數(shù)據(jù)源自動(dòng)切換)

前言

日常的業(yè)務(wù)開發(fā)項(xiàng)目中只會(huì)配置一套數(shù)據(jù)源批什,如果需要獲取其他系統(tǒng)的數(shù)據(jù)往往是通過調(diào)用接口腾降, 或者是通過第三方工具比如kettle將數(shù)據(jù)同步到自己的數(shù)據(jù)庫中進(jìn)行訪問碌奉。

但是也會(huì)有需要在項(xiàng)目中引用多數(shù)據(jù)源的場(chǎng)景劝贸。比如如下場(chǎng)景:

  • 自研數(shù)據(jù)遷移系統(tǒng)潮罪,至少需要新康谆、老兩套數(shù)據(jù)源,從老庫讀取數(shù)據(jù)寫入新庫
  • 自研讀寫分離中間件嫉到,系統(tǒng)流量增加沃暗,單庫響應(yīng)效率降低,引入讀寫分離方案何恶,寫入數(shù)據(jù)是一個(gè)數(shù)據(jù)源孽锥,讀取數(shù)據(jù)是另一個(gè)數(shù)據(jù)源

某系統(tǒng)除了需要從自己的主要數(shù)據(jù)庫上讀取和管理數(shù)據(jù)外,還有一部分業(yè)務(wù)涉及到其他多個(gè)數(shù)據(jù)庫细层,要求可以在任何方法上可以靈活指定具體要操作的數(shù)據(jù)庫惜辑。

為了在開發(fā)中以最簡單的方法使用赐纱,本文基于注解和AOP的方法實(shí)現(xiàn)授翻,在spring boot框架的項(xiàng)目中购对,添加本文實(shí)現(xiàn)的代碼類后搓译,只需要配置好數(shù)據(jù)源就可以直接通過注解使用霉祸,簡單方便蚣旱。

一墙贱、原理

關(guān)鍵類說明

忽略掉controller/service/entity/mapper/xml介紹您单。

  • jdbc.properties: 數(shù)據(jù)源配置文件实牡。雖然可以配置到Spring boot的默認(rèn)配置文件application.properties/application.yml文件當(dāng)中陌僵,但是如果數(shù)據(jù)源比較多的話,根據(jù)實(shí)際使用创坞,最佳的配置方式還是獨(dú)立配置比較好碗短。
  • DynamicDataSourceConfig:數(shù)據(jù)源配置類
  • DynamicDataSource:動(dòng)態(tài)數(shù)據(jù)源配置類
  • DataSourceRouting:動(dòng)態(tài)數(shù)據(jù)源注解
  • DynamicDataSourceAspect:動(dòng)態(tài)數(shù)據(jù)源設(shè)置切面
  • DynamicDataSourceContextHolder:當(dāng)前線程持有的數(shù)據(jù)源key
  • DataSourceConstants:數(shù)據(jù)源key常量類

開發(fā)流程

動(dòng)態(tài)數(shù)據(jù)源流程

Spring Boot 的動(dòng)態(tài)數(shù)據(jù)源,本質(zhì)上是把多個(gè)數(shù)據(jù)源存儲(chǔ)在一個(gè) Map 中题涨,當(dāng)需要使用某個(gè)數(shù)據(jù)源時(shí)偎谁,從 Map 中獲取此數(shù)據(jù)源進(jìn)行處理总滩。

在 Spring 中已提供了抽象類 AbstractRoutingDataSource 來實(shí)現(xiàn)此功能,繼承AbstractRoutingDataSource類并覆寫其determineCurrentLookupKey()方法即可巡雨,該方法只需要返回?cái)?shù)據(jù)源key即可闰渔,也就是存放數(shù)據(jù)源的Map的key。

因此铐望,我們?cè)趯?shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源的冈涧,只需要繼承它,實(shí)現(xiàn)自己的獲取數(shù)據(jù)源邏輯即可正蛙。AbstractRoutingDataSource頂級(jí)繼承了DataSource督弓,所以它也是可以做為數(shù)據(jù)源對(duì)象,因此項(xiàng)目中使用它作為主數(shù)據(jù)源乒验。

AbstractRoutingDataSource原理

AbstractRoutingDataSource中有一個(gè)重要的屬性:

  • argetDataSources:目標(biāo)數(shù)據(jù)源愚隧,即項(xiàng)目啟動(dòng)的時(shí)候設(shè)置的需要通過AbstractRoutingDataSource管理的數(shù)據(jù)源。
  • defaultTargetDataSource:默認(rèn)數(shù)據(jù)源锻全,項(xiàng)目啟動(dòng)的時(shí)候設(shè)置的默認(rèn)數(shù)據(jù)源奸攻,如果沒有指定數(shù)據(jù)源,默認(rèn)返回改數(shù)據(jù)源虱痕。
  • resolvedDataSources:也是存放的數(shù)據(jù)源,是對(duì)targetDataSources進(jìn)行處理后進(jìn)行存儲(chǔ)的辐赞〔壳蹋可以看一下源碼。
  • resolvedDefaultDataSource: 對(duì)默認(rèn)數(shù)據(jù)源進(jìn)行了二次處理响委,源碼如上圖最后的兩行代碼新思。

AbstractRoutingDataSource中所有的方法和屬性:

比較重要的是determineTargetDataSource方法。

protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    Object lookupKey = determineCurrentLookupKey();
    DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    }
    return dataSource;
}
 
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
@Nullable
protected abstract Object determineCurrentLookupKey();

這個(gè)方法主要就是返回一個(gè)DataSource對(duì)象赘风,主要邏輯就是先通過方法determineCurrentLookupKey獲取一個(gè)Object對(duì)象的lookupKey夹囚,然后通過這個(gè)lookupKey到resolvedDataSources中獲取數(shù)據(jù)源(resolvedDataSources就是一個(gè)Map,上面已經(jīng)提到過了)邀窃;如果沒有找到數(shù)據(jù)源荸哟,就返回默認(rèn)的數(shù)據(jù)源。determineCurrentLookupKey就是程序員配置動(dòng)態(tài)數(shù)據(jù)源需要自己實(shí)現(xiàn)的方法瞬捕。

二鞍历、實(shí)現(xiàn)

引入Maven依賴

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.10.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--如果要用傳統(tǒng)的xml或properties配置,則需要添加此依賴-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>

    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>2.1.5</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    <!-- swagger -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>

    <!-- spring security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- jwt -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <!-- fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.70</version>
    </dependency>


    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>

        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.6</version>
            <configuration>
                <configurationFile>
                    ${basedir}/src/main/resources/generator/generatorConfig.xml
                </configurationFile>
                <overwrite>true</overwrite>
                <verbose>true</verbose>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.41</version>
                </dependency>
                <dependency>
                    <groupId>tk.mybatis</groupId>
                    <artifactId>mapper</artifactId>
                    <version>4.1.5</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

主要實(shí)現(xiàn)步驟:一配置二使用

    1. 啟動(dòng)類注冊(cè)動(dòng)態(tài)數(shù)據(jù)源
    1. 配置文件中配置多個(gè)數(shù)據(jù)源
    1. 在需要的方法上使用注解指定數(shù)據(jù)源
  • 1肪虎、在啟動(dòng)類添加 @Import({DynamicDataSourceRegister.class, MProxyTransactionManagementConfiguration.class})

// 注冊(cè)動(dòng)態(tài)多數(shù)據(jù)源
@Import({DynamicDataSourceRegister.class})
@MapperScan("com.yibo.mapper")//掃描Mapper接口
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
  • 2劣砍、配置文件配置內(nèi)容為:
# 默認(rèn)數(shù)據(jù)源
spring.datasource.url=jdbc:mysql://localhost:3306/user_center?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.username=root
spring.datasource.hikari.password=yibo

# 更多數(shù)據(jù)源
custom.datasource.names=ds1,ds2
custom.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
custom.datasource.ds1.url=jdbc:mysql://localhost:3306/content_center?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
custom.datasource.ds1.username=root
custom.datasource.ds1.password=yibo
custom.datasource.ds2.driver-class-name=com.mysql.cj.jdbc.Driver
custom.datasource.ds2.url=jdbc:mysql://localhost:3306/trade?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
custom.datasource.ds2.username=root
custom.datasource.ds2.password=yibo

mybatis.type-aliases-package: com.yibo.center.domain.entity
mybatis.mapper-locations: classpath:mapper/*.xml
mapper.identity: MYSQL
mapper.not-empty: false

#是否激活 swagger true or false
swagger.enable=true
  • 3、使用方法
import com.yibo.center.domain.entity.Share;
import com.yibo.datasource.anno.TargetDataSource;
import com.yibo.mapper.ShareMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author: huangyibo
 * @Date: 2020/6/10 23:47
 * @Description:
 */

@Service
public class ShareService {

    @Autowired
    private ShareMapper shareMapper;

    @TargetDataSource(name = "ds1")
    @Transactional
    public List<Share> findAll(){
        return shareMapper.selectAll();
    }
}
import com.yibo.center.domain.entity.TradeGoods;
import com.yibo.center.domain.vo.TradeGoodsAO;
import com.yibo.datasource.anno.TargetDataSource;
import com.yibo.mapper.TradeGoodsMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * @author: huangyibo
 * @Date: 2020/6/11 0:23
 * @Description:
 */

@Service
public class TradeGoodsService {

    @Autowired
    private TradeGoodsMapper tradeGoodsMapper;

    @TargetDataSource(name = "ds2")
    @Transactional
    public List<TradeGoods> findAll(){
        return tradeGoodsMapper.selectAll();
    }

    @TargetDataSource(name = "ds2")
    @Transactional
    public String addTradeGoods(TradeGoodsAO tradeGoodsAO){
        TradeGoods tradeGoods = new TradeGoods();
        BeanUtils.copyProperties(tradeGoodsAO,tradeGoods);
        tradeGoods.setAddTime(new Date());
        tradeGoodsMapper.insert(tradeGoods);
        return "SUCCESS";
    }
}
import com.yibo.center.domain.entity.User;
import com.yibo.center.domain.vo.UserAo;
import com.yibo.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * @author: huangyibo
 * @Date: 2020/6/10 23:46
 * @Description:
 */

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<User> findAll(){
        return userMapper.selectAll();
    }

    @Transactional
    public User findById(Integer id){
        User user = new User();
        user.setId(id);
        return userMapper.selectOne(user);
    }

    @Transactional
    public String addUser(UserAo userAo){
        User user = new User();
        BeanUtils.copyProperties(userAo,user);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        userMapper.insert(user);
        return "SUCCESS";
    }
}

要注意的是扇救,在使用MyBatis時(shí)刑枝,注解@TargetDataSource 不能直接在接口類Mapper上使用香嗓。

請(qǐng)將下面幾個(gè)類放到Spring Boot項(xiàng)目中。

  • DynamicDataSource.java
  • DynamicDataSourceAspect.java
  • DynamicDataSourceContextHolder.java
  • DynamicDataSourceRegister.java
  • TargetDataSource.java
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author: huangyibo
 * @Date: 2020/6/10 22:29
 * @Description: 繼承Spring AbstractRoutingDataSource實(shí)現(xiàn)路由切換
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
import com.yibo.datasource.DynamicDataSourceContextHolder;
import com.yibo.datasource.anno.TargetDataSource;
import lombok.extern.slf4j.Slf4j;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author: huangyibo
 * @Date: 2020/6/10 22:28
 * @Description: 動(dòng)態(tài)數(shù)據(jù)源通知
 */
@Aspect
//保證該AOP在@Transactional之前執(zhí)行
@Order(-1)
@Component
@Slf4j
public class DynamicDataSourceAspect {

    /**
     * @Description 在方法執(zhí)行之前執(zhí)行  @annotation(ds) 會(huì)攔截有ds這個(gè)注解的方法即有 TargetDataSource這個(gè)注解的
     * @param @param point
     * @param @param ds
     * @param @throws Throwable 參數(shù)
     * @return void 返回類型
     * @throws
     */
    @Before("@annotation(ds)")
    public void changeDataSource(JoinPoint point, TargetDataSource ds)
            throws Throwable {
        String dsId = ds.name();
        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
            log.error("數(shù)據(jù)源[{}]不存在装畅,使用默認(rèn)數(shù)據(jù)源 > {}", ds.name(), point.getSignature());
        }
        else {
            log.debug("Use DataSource : {} > {}", ds.name(),point.getSignature());
            DynamicDataSourceContextHolder.setDataSourceType(ds.name());
        }
    }

    /**
     * @Description 在方法執(zhí)行之后執(zhí)行  @annotation(ds) 會(huì)攔截有ds這個(gè)注解的方法即有 TargetDataSource這個(gè)注解的
     * @param @param point
     * @param @param ds 參數(shù)
     * @return void 返回類型
     * @throws
     */
    @After("@annotation(ds)")
    public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
        log.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}
import java.util.ArrayList;
import java.util.List;

/**
 * @author: huangyibo
 * @Date: 2020/6/10 22:25
 * @Description: 動(dòng)態(tài)數(shù)據(jù)源上下文管理
 */

public class DynamicDataSourceContextHolder {

    //存放當(dāng)前線程使用的數(shù)據(jù)源類型信息
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    //存放數(shù)據(jù)源id
    public static List<String> dataSourceIds = new ArrayList<String>();

    //設(shè)置數(shù)據(jù)源
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    //獲取數(shù)據(jù)源
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    //清除數(shù)據(jù)源
    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    /**
     * 判斷指定DataSrouce當(dāng)前是否存在
     *
     * @param dataSourceId
     * @return
     */
    public static boolean containsDataSource(String dataSourceId){
        return dataSourceIds.contains(dataSourceId);
    }
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

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

/**
 * @author: huangyibo
 * @Date: 2020/6/10 22:10
 * @Description: 注冊(cè)動(dòng)態(tài)數(shù)據(jù)源
 *  初始化數(shù)據(jù)源和提供了執(zhí)行動(dòng)態(tài)切換數(shù)據(jù)源的工具類
 *  EnvironmentAware(獲取配置文件配置的屬性值)
 */

@Slf4j
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    //指定默認(rèn)數(shù)據(jù)源(springboot2.0默認(rèn)數(shù)據(jù)源是hikari如何想使用其他數(shù)據(jù)源可以自己配置)
    private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";

    //默認(rèn)數(shù)據(jù)源
    private DataSource defaultDataSource;

    //用戶自定義數(shù)據(jù)源
    private Map<String, DataSource> customDataSources  = new HashMap<>();

    /**
     * 加載多數(shù)據(jù)源配置
     * @param env
     */
    @Override
    public void setEnvironment(Environment env) {
        initDefaultDataSource(env);
        initCustomDataSources(env);
    }



    /**
     * 初始化主數(shù)據(jù)源
     * @param env
     */
    private void initDefaultDataSource(Environment env) {
        // 讀取主數(shù)據(jù)源
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("driver", env.getProperty("spring.datasource.hikari.driver-class-name"));
        dsMap.put("url", env.getProperty("spring.datasource.url"));
        dsMap.put("username", env.getProperty("spring.datasource.hikari.username"));
        dsMap.put("password", env.getProperty("spring.datasource.hikari.password"));
        defaultDataSource = buildDataSource(dsMap);
    }


    /**
     * 初始化更多數(shù)據(jù)源
     * @param env
     */
    private void initCustomDataSources(Environment env) {
        // 讀取配置文件獲取更多數(shù)據(jù)源
        String dsPrefixs = env.getProperty("custom.datasource.names");
        for (String dsPrefix : dsPrefixs.split(",")) {
            // 多個(gè)數(shù)據(jù)源
            Map<String, Object> dsMap = new HashMap<>();
            dsMap.put("driver", env.getProperty("custom.datasource." + dsPrefix + ".driver-class-name"));
            dsMap.put("url", env.getProperty("custom.datasource." + dsPrefix + ".url"));
            dsMap.put("username", env.getProperty("custom.datasource." + dsPrefix + ".username"));
            dsMap.put("password", env.getProperty("custom.datasource." + dsPrefix + ".password"));
            DataSource ds = buildDataSource(dsMap);
            customDataSources.put(dsPrefix, ds);
        }
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        // 將主數(shù)據(jù)源添加到更多數(shù)據(jù)源中
        targetDataSources.put("dataSource", defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
        // 添加更多數(shù)據(jù)源
        targetDataSources.putAll(customDataSources);
        for (String key : customDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIds.add(key);
        }

        // 創(chuàng)建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        registry.registerBeanDefinition("dataSource", beanDefinition); // 注冊(cè)到Spring容器中

        log.info("Dynamic DataSource Registry");
    }

    /**
     * 創(chuàng)建DataSource
     * @param dsMap
     * @return
     */
    @SuppressWarnings("unchecked")
    public DataSource buildDataSource(Map<String, Object> dsMap) {
        try {
            Object type = dsMap.get("type");
            if (type == null)
                type = DATASOURCE_TYPE_DEFAULT;// 默認(rèn)DataSource

            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>)Class.forName((String)type);
            log.info("dsMap:{}",dsMap);
            System.out.println(dsMap);
            String driverClassName = dsMap.get("driver").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();
            // 自定義DataSource配置
            DataSourceBuilder factory = DataSourceBuilder.create()
                    .driverClassName(driverClassName)
                    .url(url)
                    .username(username)
                    .password(password)
                    .type(dataSourceType);
            return factory.build();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
import java.lang.annotation.*;

/**
 * @author: huangyibo
 * @Date: 2020/6/10 22:27
 * @Description: 作用于類靠娱、接口或者方法上
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String name();
}

本文代碼博主是經(jīng)過測(cè)試后沒有問題才發(fā)出來共享給大家的。對(duì)于連接池參數(shù)配置會(huì)應(yīng)用到所有數(shù)據(jù)源上洁灵。

比如配置一個(gè):

spring.datasource.maximum-pool-size=80

那么我們所有的數(shù)據(jù)源都會(huì)自動(dòng)應(yīng)用上饱岸。

補(bǔ)充:

如果你使用的是SpringMVC,并集成了Shiro徽千,一般按網(wǎng)上的配置你可能是:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
    <property name="proxyTargetClass" value="true" />
</bean>

<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean>

那么你請(qǐng)不要這樣做苫费,請(qǐng)按下面方法配置:

<!-- AOP式方法級(jí)權(quán)限檢查  -->
<!-- 不要使用 DefaultAdvisorAutoProxyCreator 會(huì)出現(xiàn)二次代理的問題,這里不詳述双抽。 mark by shanhy 2016-05-15 -->
<aop:config proxy-target-class="true"/>
<!-- 或者你使用了 <aop:aspectj-autoproxy proxy-target-class="true" /> 也可以百框。 -->

<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean>

參考:
https://blog.51cto.com/binghe001/5243610

https://blog.csdn.net/LBWNB_Java/article/details/126115608

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市牍汹,隨后出現(xiàn)的幾起案子铐维,更是在濱河造成了極大的恐慌,老刑警劉巖慎菲,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫁蛇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡露该,警方通過查閱死者的電腦和手機(jī)睬棚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來解幼,“玉大人抑党,你說我怎么就攤上這事∧彀冢” “怎么了底靠?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長特铝。 經(jīng)常有香客問我暑中,道長,這世上最難降的妖魔是什么鲫剿? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任痒芝,我火速辦了婚禮,結(jié)果婚禮上牵素,老公的妹妹穿的比我還像新娘严衬。我一直安慰自己,他們只是感情好笆呆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布请琳。 她就那樣靜靜地躺著粱挡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俄精。 梳的紋絲不亂的頭發(fā)上询筏,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音竖慧,去河邊找鬼嫌套。 笑死,一個(gè)胖子當(dāng)著我的面吹牛圾旨,可吹牛的內(nèi)容都是我干的踱讨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼砍的,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼痹筛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起廓鞠,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤帚稠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后床佳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滋早,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年砌们,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杆麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怨绣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拷获,到底是詐尸還是另有隱情篮撑,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布匆瓜,位于F島的核電站赢笨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏驮吱。R本人自食惡果不足惜茧妒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望左冬。 院中可真熱鬧桐筏,春花似錦、人聲如沸拇砰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至牧氮,卻和暖如春琼腔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踱葛。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工丹莲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尸诽。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓甥材,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逊谋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子擂达,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容