前言
日常的業(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)步驟:一配置二使用
- 啟動(dòng)類注冊(cè)動(dòng)態(tài)數(shù)據(jù)源
- 配置文件中配置多個(gè)數(shù)據(jù)源
- 在需要的方法上使用注解指定數(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>