SpirngBoot druid自定义注解使用多数据源
1.基础配置
注解
自定义注解可以标识在类上或者方法上从而让程序运行时选择响应的数据源。
package cn.rui.annotation;
import cn.rui.enums.DataSourceTypes;
import java.lang.annotation.*;
/**
* @author rui
*
* 多数据源切换注解
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DataSources {
//默认为主数据源
DataSourceTypes value() default DataSourceTypes.MASTER;
}
常量标识
package cn.rui.enums;
/**
* @author rui
*
* 数据源类型
*/
public enum DataSourceTypes {
/**
* 主数据源(数据源1)
*/
MASTER,
/**
* 从数据源(数据源2),可以继续定义更多
*/
SLAVE
}
配置文件
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://xxx/xxx?characterEncoding=UTF-8
username:
password:
# 从库数据源
slave:
# 从数据源开关/默认关闭 ---该参数未配置,如需使用请参考@ConditionalOnProperty
enabled: true
url: jdbc:mysql://xxx/xxx?characterEncoding=UTF-8
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username:
login-password:
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
2.定义数据源切换类
Threadlocal存取数据源
package cn.rui.common.datasource;
/**
* @author rui
*
* 数据源切换方法
*/
public class DynamicDataSourceContextHolder {
/**
* 强引用Threadlocal,单线程独立变量
*/
private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal();
/**
* 存入数据源的引用名
* @param dsName
*/
public static void setDataSource(String dsName){
DATASOURCE_HOLDER.set(dsName);
}
/**
* 得到数据源的引用名
* @return
*/
public static String getDataSource(){
return DATASOURCE_HOLDER.get();
}
/**
* 清空变量,防止业务出错以及内存泄漏
*/
public static void clearDataSource(){
DATASOURCE_HOLDER.remove();
}
}
获取数据源
determineCurrentLookupKey()用来获取当前应使用哪个数据源。
package cn.rui.common.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* @author rui
* 获取数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource
{
public DynamicDataSource(DataSource defaultDataSource, Map<Object,Object> targetDataSource) {
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(targetDataSource);
super.afterPropertiesSet();
}
/**
* 选择数据源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSource();
}
}
切面类切入数据源
package cn.rui.aop;
import cn.rui.annotation.DataSources;
import cn.rui.common.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author rui
*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
@Pointcut("@annotation(cn.rui.annotation.DataSources)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSources dataSource = method.getAnnotation(DataSources.class);
if (null!=dataSource)
{
DynamicDataSourceContextHolder.setDataSource(dataSource.value().name());
}
try
{
return point.proceed();
}
finally
{
// 销毁数据源 在执行方法之后 ###重要###
DynamicDataSourceContextHolder.clearDataSource();
}
}
}
3.配置druid
druid配置类
package cn.rui.config;
import cn.rui.common.datasource.DynamicDataSource;
import cn.rui.enums.DataSourceTypes;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @author rui
* 德鲁伊
*/
@Configuration
public class DruidConfig {
@Autowired
DruidProperties druidProperties;
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDatasource(){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource salveDatasource(){
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean("dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDatasource){
Map<Object,Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceTypes.MASTER.name(),masterDatasource);
targetDataSources.put(DataSourceTypes.SLAVE.name(),salveDatasource());
return new DynamicDataSource(masterDatasource,targetDataSources);
}
}
注入自定义配置
package cn.rui.config;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author rui
* 注入自定义配置
*/
@Configuration
@ConfigurationProperties("spring.datasource.druid")
@Data
public class DruidProperties {
private int initialSize;
private int minIdle;
private int maxActive;
private int maxWait;
private int timeBetweenEvictionRunsMillis;
private int minEvictableIdleTimeMillis;
private int maxEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource druidDataSource){
druidDataSource.setInitialSize(this.initialSize);
druidDataSource.setMinIdle(this.minIdle);
druidDataSource.setMaxActive(this.maxActive);
druidDataSource.setMaxWait(this.maxWait);
druidDataSource.setTimeBetweenEvictionRunsMillis(this.timeBetweenEvictionRunsMillis);
druidDataSource.setMinEvictableIdleTimeMillis(this.minEvictableIdleTimeMillis);
druidDataSource.setMaxEvictableIdleTimeMillis(this.maxEvictableIdleTimeMillis);
druidDataSource.setValidationQuery(this.validationQuery);
druidDataSource.setTestWhileIdle(this.testWhileIdle);
druidDataSource.setTestOnBorrow(this.testOnBorrow);
druidDataSource.setTestOnReturn(this.testOnReturn);
return druidDataSource;
}
}
取消自定义注入
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
至此为止,自定义注解多数据源完成。
使用方式:
package cn.rui.dao;
import cn.rui.annotation.DataSources;
import cn.rui.enums.DataSourceTypes;
import java.util.List;
public interface GeoGisMapper{
List<Object> getSource1();
@DataSources(DataSourceTypes.SLAVE)
List<Object> getSource2();
}
本教程只支持druid多数据源,其它连接池前两步思路相同,配置注入部分需要结合springboot进行绑定。