初始化提交

This commit is contained in:
2021-01-20 18:27:28 +08:00
commit e2c592fd4f
29 changed files with 1504 additions and 0 deletions

View File

@ -0,0 +1,18 @@
package business.cooperop.base.datasource.mysql.common;
/*
**********************************************
* DATE PERSON REASON
* 2020/7/24 FXY Created
**********************************************
*/
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MultiDataSource {
String name() default "main";
}

View File

@ -0,0 +1,121 @@
package business.cooperop.base.datasource.mysql.config;
/*
**********************************************
* DATE PERSON REASON
* 2020/7/24 FXY Created
**********************************************
*/
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.JdbcConstants;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
@Component
@ConfigurationProperties(prefix = "spring.datasource")
@Setter
@Getter
@Slf4j
public class DruidProperties {
public DruidProperties() {
log.info("default 数据源加载");
}
/**
* 数据源名称
*/
private String datasourceName = "main";
private String url;
private String username;
private String password;
/**
* 默认为 MYSQL 8.x 配置
*/
private String driverClassName;
private Integer initialSize = 10;
private Integer minIdle = 3;
private Integer maxActive = 60;
private Integer maxWait = 60000;
private Boolean removeAbandoned = true;
private Integer removeAbandonedTimeout = 180;
private Integer timeBetweenEvictionRunsMillis = 60000;
private Integer minEvictableIdleTimeMillis = 300000;
private String validationQuery = "SELECT 'x'";
private Boolean testWhileIdle = true;
private Boolean testOnBorrow = false;
private Boolean testOnReturn = false;
private Boolean poolPreparedStatements = true;
private Integer maxPoolPreparedStatementPerConnectionSize = 50;
private String filters = "stat";
public DruidDataSource config() {
DruidDataSource dataSource = new DruidDataSource();
return config(dataSource);
}
public DruidDataSource config(DruidDataSource dataSource) {
dataSource.setDbType(JdbcConstants.SQL_SERVER);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
dataSource.setInitialSize(initialSize); // 定义初始连接数
dataSource.setMinIdle(minIdle); // 最小空闲
dataSource.setMaxActive(maxActive); // 定义最大连接数
dataSource.setMaxWait(maxWait); // 获取连接等待超时的时间
dataSource.setRemoveAbandoned(removeAbandoned); // 超过时间限制是否回收
dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout); // 超过时间限制多长
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
// 配置一个连接在池中最小生存的时间,单位是毫秒
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
// 用来检测连接是否有效的sql要求是一个查询语句
dataSource.setValidationQuery(validationQuery);
// 申请连接的时候检测
dataSource.setTestWhileIdle(testWhileIdle);
// 申请连接时执行validationQuery检测连接是否有效配置为true会降低性能
dataSource.setTestOnBorrow(testOnBorrow);
// 归还连接时执行validationQuery检测连接是否有效配置为true会降低性能
dataSource.setTestOnReturn(testOnReturn);
// 打开PSCache并且指定每个连接上PSCache的大小
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
// 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
// 监控统计用的filter:stat
// 日志用的filter:log4j
// 防御SQL注入的filter:wall
try {
dataSource.setFilters(filters);
} catch (SQLException e) {
log.error("扩展插件失败.{}", e.getMessage());
}
return dataSource;
}
}

View File

@ -0,0 +1,52 @@
package business.cooperop.base.datasource.mysql.config;
/*
**********************************************
* DATE PERSON REASON
* 2020/7/24 FXY Created
**********************************************
*/
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* <p>多 datasource 的上下文</p>
* <p>每个线程独立的数据库连接名称</p>
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* @param dataSourceDbName 数据库别名
* @Description: 设置数据源别名
*/
public static void setDataSourceDbName(String dataSourceDbName) {
contextHolder.set(dataSourceDbName);
}
/**
* @Description: 获取数据源别名
*/
public static String getDataSourceDbName() {
return contextHolder.get();
}
/**
* @Description: 清除数据源别名
*/
public static void clearDataSourceDbName() {
contextHolder.remove();
}
/**
* 重写获取连接名称的方法
*
* @return 连接名称
*/
@Override
protected Object determineCurrentLookupKey() {
return getDataSourceDbName();
}
}

View File

@ -0,0 +1,76 @@
package business.cooperop.base.datasource.mysql.config;
/*
**********************************************
* DATE PERSON REASON
* 2020/7/24 FXY Created
**********************************************
*/
import business.cooperop.base.datasource.mysql.common.MultiDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
@ConditionalOnProperty(prefix = "business.cooperop.base", name = "multiDatasourceOpen", havingValue = "true")
@Slf4j
public class MultiDataSourceAop implements Ordered {
public MultiDataSourceAop() {
log.info("多数据源初始化 AOP ");
}
@Pointcut(value = "@annotation(business.cooperop.base.datasource.mysql.common.MultiDataSource)")
private void cut() {
}
@Around("cut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Signature signature = point.getSignature();
MethodSignature methodSignature;
if (!(signature instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
methodSignature = (MethodSignature) signature;
//获取当前方法的注解
Object target = point.getTarget();
Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
MultiDataSource datasource = currentMethod.getAnnotation(MultiDataSource.class);
if (datasource != null) {
DynamicDataSource.setDataSourceDbName(datasource.name());
log.info("设置数据源为:" + datasource.name());
} else {
DynamicDataSource.setDataSourceDbName("main");
log.info("设置数据源为:默认 --> main");
}
try {
return point.proceed();
} finally {
log.info("清空数据源信息!");
DynamicDataSource.clearDataSourceDbName();
}
}
/**
* aop的顺序要早于spring的事务
*/
@Override
public int getOrder() {
return 1;
}
}

View File

@ -0,0 +1,33 @@
package business.cooperop.base.datasource.mysql.config;
/*
**********************************************
* DATE PERSON REASON
* 2020/7/24 FXY Created
**********************************************
*/
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "business.cooperop.base")
@Getter
@Setter
@Slf4j
public class MultiDataSourceConfig {
public MultiDataSourceConfig() {
log.info("加载多数据源配置信息 --> {}", "business.cooperop.base");
}
/**
* 多个数据源
*/
private List<DruidProperties> datasource;
}

View File

@ -0,0 +1,89 @@
package business.cooperop.base.datasource.mysql.config;
/*
**********************************************
* DATE PERSON REASON
* 2020/7/24 FXY Created
**********************************************
*/
import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@Configuration
public class MultiSourceConfig {
@Autowired
private DruidProperties druidProperties;
@Autowired
private MultiDataSourceConfig multiDataSourceConfig;
/**
* 单数据源连接池配置
*/
@Bean
@ConditionalOnProperty(name = "business.cooperop.base.multiDatasourceOpen", havingValue = "false")
public DruidDataSource singleDatasource() {
log.info("singleDatasource");
return druidProperties.config(new DruidDataSource());
}
/**
* 多数据源连接池配置
*/
@Bean
@ConditionalOnProperty(name = "business.cooperop.base.multiDatasourceOpen", havingValue = "true")
public DynamicDataSource mutiDataSource() {
log.info("mutiDataSource");
//存储数据源别名与数据源的映射
HashMap<Object, Object> dbNameMap = new HashMap();
// 核心数据源
DruidDataSource mainDataSource = druidProperties.config();
// 这里添加 主要数据库,其它数据库挂了,默认使用主数据库
dbNameMap.put("main", mainDataSource);
// 其它数据源
// 当前多数据源是否存在
if (multiDataSourceConfig.getDatasource() != null) {
//过滤掉没有添加 datasourceName 的数据源,先加载娟全局配置,再次加载当前配置
List<DruidDataSource> multiDataSourceList = multiDataSourceConfig.getDatasource().stream()
.filter(dp -> !"".equals(Optional.ofNullable(dp.getDatasourceName()).orElse("")))
.map(dp -> {
DruidDataSource druidDataSource = dp.config(druidProperties.config());
dbNameMap.put(dp.getDatasourceName(), druidDataSource);
return druidDataSource;
})
.collect(Collectors.toList());
// 测试所有的数据源
try {
mainDataSource.init();
for (DruidDataSource druidDataSource : multiDataSourceList) {
druidDataSource.init();
}
} catch (SQLException sql) {
log.error("======================= 多数据源配置错误 ==========================");
sql.printStackTrace();
}
}
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(dbNameMap);
dynamicDataSource.setDefaultTargetDataSource(mainDataSource);
return dynamicDataSource;
}
}