注:SpringBoot会自动装备bean名为dataSource
在我们知道了SpringBoot会自动装配bean名为dataSource的时候,最基础的但数据源配置文件装配的形式就变成了
@Configuration
public class DataSourceConfig {
@Bean(name = "dataSource")
public DataSource dataSource() {
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setUser("root");
ds.setPassword("****");
ds.setJdbcUrl("jdbc:mysql://****:3306/****?verifyServerCertificate=false&useUnicode=true&useSSL=false");
//如果 breakAfterAcquireFailure=true ,一旦pool向数据库请求连接失败,就会标记pool block并关闭pool,
//这样无论数据库是否恢复正常,应用端都无法从pool拿到连接
ds.setBreakAfterAcquireFailure(false);
//连接池中保留的最小连接数,默认为3
ds.setMinPoolSize(20);
//连接池中保留的最大连接数。默认值15
ds.setMaxPoolSize(50);
//初始化连接池中的连接数 大于等于 minPoolSize
ds.setInitialPoolSize(20);
//最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0
ds.setMaxIdleTime(60);
//表示 connection能存活的绝对时间
ds.setMaxConnectionAge(0);
//当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0
ds.setCheckoutTimeout(3000);
//当连接池中的连接耗尽的时候c3p0 一次同时获取的连接数,默认值: 3
ds.setAcquireIncrement(10);
//定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次
ds.setAcquireRetryAttempts(0);
//重新尝试的时间间隔,默认为:1000毫秒
ds.setAcquireRetryDelay(2000);
//每60秒检查所有连接池中的空闲连接。默认值: 0,不检查
ds.setIdleConnectionTestPeriod(60);
//c3p0将建一张名为C3p0TestTable_NotDelete的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。
//你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null
ds.setAutomaticTestTable("C3p0TestTable_NotDelete");
//关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务
ds.setAutoCommitOnClose(false);
//true表示在每次从pool内checkout连接的时候测试其有效性,这是个同步操作,因此应用端的每次数据库调用,
//都会先通过测试sql测试其有效性,如果连接无效,会关闭此连接并剔除出pool,
//并尝试从pool内取其他连接,默认为false,此特性要慎用,会造成至少多一倍的数据库调用。
ds.setTestConnectionOnCheckout(false);
//true表示每次把连接checkin到pool里的时候测试其有效性,
//因为是个事后操作,所以是异步的,应用端不需要等待测试结果,但同样会造成至少多一倍的数据库调用。
ds.setTestConnectionOnCheckin(false);
ds.setNumHelperThreads(3);
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
return ds;
}
}
那么在这种情况下,如果我们想装配多个数据源的时候,可以根据思路延申成为
@Configuration
public class DataSourceConfig {
@Bean("dbcommonMaster")
@ConfigurationProperties(prefix = "dbcommon.master")
public DataSource dbcommonMaster() {
return DataSourceBuilder.create().type(ComboPooledDataSource.class).build();
}
@Bean("dbcommonSlave")
@ConfigurationProperties(prefix = "dbcommon.slave")
public DataSource dbcommonSlave() {
return DataSourceBuilder.create().type(ComboPooledDataSource.class).build();
}
@Bean("dataSource")
public DataSource dbcommonMasterSlaveDataSource() {
MasterSlaveDataSource masterSlaveDataSource = new MasterSlaveDataSource();
Map<Object,Object> targetDataSources = new HashMap<>();
targetDataSources.put(Type.Master.value(), dbcommonMaster());
targetDataSources.put(Type.Slave.value(), dbcommonSlave());
masterSlaveDataSource.setTargetDataSources(targetDataSources);
masterSlaveDataSource.setDefaultTargetDataSource(dbcommonMaster());
return masterSlaveDataSource;
}
}
而在配置文件中,则使用
dbcommon.master.jdbcUrl=jdbc:mysql://127.0.0.1:3315/****?verifyServerCertificate=false&useUnicode=true&useSSL=false
dbcommon.master.user=root
dbcommon.master.password=***
dbcommon.master.driverClass=com.mysql.jdbc.Driver
dbcommon.master.minPoolSize=20
dbcommon.master.maxPoolSize=50
dbcommon.master.initialPoolSize=20
dbcommon.master.maxIdleTime=60
dbcommon.master.maxConnectionAge=0
dbcommon.master.checkoutTimeout=3000
dbcommon.master.acquireIncrement=10
dbcommon.master.acquireRetryAttempts=0
dbcommon.master.acquireRetryDelay=2000
dbcommon.master.maxStatements=1000
dbcommon.master.idleConnectionTestPeriod=60
dbcommon.master.automaticTestTable=C3p0TestTable_NotDelete
dbcommon.master.autoCommitOnClose=false
dbcommon.master.breakAfterAcquireFailure=false
dbcommon.master.testConnectionOnCheckout=false
这种形式进行配置,有几个,就配置几套.dbcommon.master是根据你的ConfigurationProperties注解中的名字进行装配的
其中的枚举代码:
public enum Type {
Master("Master"),
Slave("Slave");
private final String value;
Type(String value) {
this.value = value;
}
public String value() {
return this.value;
}
}
MasterSlaveDataSource代码为
public class MasterSlaveDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
String type = DataSourceChoose.getType();
if(log.isDebugEnabled()) {
log.debug("MasterSlaveDataSource choose type is [" + type + "]");
}
return type;
}
}
在这里可以可定义一个注解,考虑使用哪个数据源进行操作
注解切面:
@Slf4j
@Aspect
@Component
public class DataSourceAspect {
@Around("execution(public * com.framework.code.service..*.*(..)) && " +
"@annotation(com.framework.core.db.DataSource)")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
DataSource db = method.getAnnotation(DataSource.class);
DataSourceChoose.setType(db.type());
DataSourceChoose.setDBName(db.dbName());
return point.proceed();
} finally {
DataSourceChoose.clean();
}
}
}
定义DataSourceChoose
public class DataSourceChoose {
private static final ThreadLocal<String> MasterSlave = new ThreadLocal<>();
private static final ThreadLocal<String> DBName = new ThreadLocal<>();
private static final ThreadLocal<Long> RoutingKey = new ThreadLocal<>();
public static void setType(Type type) {
MasterSlave.set(type.value());
}
public static void setDBName(DBName name) {
DBName.set(name.value());
}
public static void setRoutingKey(Long routingKey) {
RoutingKey.set(routingKey);
}
public static String getType() {
return MasterSlave.get();
}
public static String getDBName() {
return DBName.get();
}
public static Long getRoutingKey() {
return RoutingKey.get();
}
public static void clean() {
MasterSlave.remove();
DBName.remove();
RoutingKey.remove();
}
}
注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
Type type() default Type.Master;
String routingKey() default "";
DBName dbName();
}
注解默认走主库,经过切面赋值以后.获取AbstractRoutingDataSource中的determineCurrentLookupKey,如果没有值,则是默认,如果有值,在切面中赋值get获取之后,AbstractRoutingDataSource类中的方法,会获取到数据源.setTargetDataSources方法中的下标.进行数据源确定,进而确认数据源,在确认数据源之后,使用完毕移除已经存在的数据源,在切面中已经体现