Skip to content

Commit 59038f6

Browse files
committed
集成ApiBoot DataSource Switch 多数据源切换
1 parent 32cf479 commit 59038f6

File tree

16 files changed

+807
-0
lines changed

16 files changed

+807
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>api-boot-plugins</artifactId>
7+
<groupId>org.minbox.framework</groupId>
8+
<version>1.0.3.RELEASE</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>api-boot-plugin-datasource-switch</artifactId>
13+
<properties>
14+
<main.basedir>${basedir}/../../..</main.basedir>
15+
</properties>
16+
<description>
17+
ApiBoot DataSource Switch
18+
数据源切换:
19+
1. 多数据源配置
20+
2. 数据源路由器实现
21+
3. 配置使用指定数据源
22+
4. 动态使用指定数据源
23+
5. 动态创建数据源
24+
</description>
25+
<dependencies>
26+
<!--ApiBoot Plugin-->
27+
<dependency>
28+
<groupId>org.minbox.framework</groupId>
29+
<artifactId>api-boot-plugin</artifactId>
30+
</dependency>
31+
<!--HikariCP DataSource-->
32+
<dependency>
33+
<groupId>com.zaxxer</groupId>
34+
<artifactId>HikariCP</artifactId>
35+
<optional>true</optional>
36+
</dependency>
37+
<!--Druid DataSource-->
38+
<dependency>
39+
<groupId>com.alibaba</groupId>
40+
<artifactId>druid</artifactId>
41+
<optional>true</optional>
42+
</dependency>
43+
<!--SpringBoot Jdbc-->
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-starter-jdbc</artifactId>
47+
<optional>true</optional>
48+
</dependency>
49+
50+
</dependencies>
51+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.minbox.framework.api.boot.plugin.datasource;
2+
3+
import org.minbox.framework.api.boot.common.exception.ApiBootException;
4+
5+
import javax.sql.DataSource;
6+
7+
/**
8+
* Api Boot DataSource 接口定义
9+
*
10+
* @author:恒宇少年 - 于起宇
11+
* <p>
12+
* DateTime:2019-04-01 14:55
13+
* Blog:http://blog.yuqiyu.com
14+
* WebSite:http://www.jianshu.com/u/092df3f77bca
15+
* Gitee:https://gitee.com/hengboy
16+
* GitHub:https://github.com/hengboy
17+
*/
18+
public interface ApiBootDataSource extends DataSource {
19+
/**
20+
* Create new data source Instance
21+
*
22+
* @return DataSource
23+
* @throws ApiBootException 异常信息
24+
*/
25+
DataSource build() throws ApiBootException;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.minbox.framework.api.boot.plugin.datasource;
2+
3+
import org.minbox.framework.api.boot.plugin.datasource.config.DataSourceConfig;
4+
import org.minbox.framework.api.boot.plugin.datasource.config.DataSourceDruidConfig;
5+
import org.minbox.framework.api.boot.plugin.datasource.config.DataSourceHikariConfig;
6+
import org.minbox.framework.api.boot.plugin.datasource.support.ApiBootBasicDataSource;
7+
import org.minbox.framework.api.boot.plugin.datasource.support.ApiBootDruidDataSource;
8+
import org.minbox.framework.api.boot.plugin.datasource.support.ApiBootHikariDataSource;
9+
10+
import javax.sql.DataSource;
11+
12+
/**
13+
* ApiBoot DataSource Switch
14+
* data source factory
15+
*
16+
* @author:恒宇少年 - 于起宇
17+
* <p>
18+
* DateTime:2019-04-01 11:32
19+
* Blog:http://blog.yuqiyu.com
20+
* WebSite:http://www.jianshu.com/u/092df3f77bca
21+
* Gitee:https://gitee.com/hengboy
22+
* GitHub:https://github.com/hengboy
23+
*/
24+
public class ApiBootDataSourceFactoryBean {
25+
/**
26+
* create new dataSource instance
27+
*
28+
* @param config dataSource config
29+
* @return dataSource Instance
30+
*/
31+
public DataSource newDataSource(DataSourceConfig config) {
32+
DataSource dataSource = null;
33+
// not setting data source type class name
34+
if (config.getDataSourceType() == null) {
35+
// use druid data source
36+
if (checkUseAppointDataSource(DataSourceTypeNames.DRUID)) {
37+
dataSource = new ApiBootDruidDataSource((DataSourceDruidConfig) config);
38+
}
39+
// use Hikari data source
40+
else if (checkUseAppointDataSource(DataSourceTypeNames.HIKARI)) {
41+
dataSource = new ApiBootHikariDataSource((DataSourceHikariConfig) config);
42+
}
43+
}
44+
// setting data source type class name
45+
else {
46+
// druid data source
47+
if (DataSourceTypeNames.DRUID.equals(config.getDataSourceType().getName())) {
48+
dataSource = new ApiBootDruidDataSource((DataSourceDruidConfig) config);
49+
}
50+
// Hikari data source
51+
else if (DataSourceTypeNames.HIKARI.equals(config.getDataSourceType().getName())) {
52+
dataSource = new ApiBootHikariDataSource((DataSourceHikariConfig) config);
53+
}
54+
}
55+
// use default basic data source
56+
if (dataSource == null) {
57+
dataSource = new ApiBootBasicDataSource(config);
58+
}
59+
return dataSource;
60+
}
61+
62+
/**
63+
* check project is use appoint data source
64+
*
65+
* @return true/false
66+
*/
67+
private boolean checkUseAppointDataSource(String dataSourceTypeName) {
68+
boolean isUseCheck = true;
69+
try {
70+
Class.forName(dataSourceTypeName);
71+
} catch (ClassNotFoundException e) {
72+
isUseCheck = false;
73+
}
74+
return isUseCheck;
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.minbox.framework.api.boot.plugin.datasource;
2+
3+
/**
4+
* ApiBoot DataSource Switch 所支持的数据源类型限定名
5+
*
6+
* @author:恒宇少年 - 于起宇
7+
* <p>
8+
* DateTime:2019-04-01 11:33
9+
* Blog:http://blog.yuqiyu.com
10+
* WebSite:http://www.jianshu.com/u/092df3f77bca
11+
* Gitee:https://gitee.com/hengboy
12+
* GitHub:https://github.com/hengboy
13+
*/
14+
public interface DataSourceTypeNames {
15+
/**
16+
* Druid 数据源类全限定路径
17+
*/
18+
String DRUID = "com.alibaba.druid.pool.DruidDataSource";
19+
/**
20+
* Hikari 数据源全限定路径
21+
*/
22+
String HIKARI = "com.zaxxer.hikari.HikariDataSource";
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.minbox.framework.api.boot.plugin.datasource.annotation;
2+
3+
import java.lang.annotation.*;
4+
5+
/**
6+
* data source switch annotation
7+
*
8+
* @author:恒宇少年 - 于起宇
9+
* <p>
10+
* DateTime:2019-04-01 16:23
11+
* Blog:http://blog.yuqiyu.com
12+
* WebSite:http://www.jianshu.com/u/092df3f77bca
13+
* Gitee:https://gitee.com/hengboy
14+
* GitHub:https://github.com/hengboy
15+
*/
16+
@Retention(RetentionPolicy.RUNTIME)
17+
@Target({ElementType.TYPE, ElementType.METHOD})
18+
@Documented
19+
public @interface DataSourceSwitch {
20+
/**
21+
* data source pool name
22+
*
23+
* @return pool name
24+
*/
25+
String value();
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.minbox.framework.api.boot.plugin.datasource.aop.advistor;
2+
3+
import org.aopalliance.aop.Advice;
4+
import org.minbox.framework.api.boot.plugin.datasource.annotation.DataSourceSwitch;
5+
import org.minbox.framework.api.boot.plugin.datasource.aop.interceptor.ApiBootDataSourceSwitchAnnotationInterceptor;
6+
import org.springframework.aop.Pointcut;
7+
import org.springframework.aop.support.AbstractPointcutAdvisor;
8+
import org.springframework.aop.support.ComposablePointcut;
9+
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
10+
import org.springframework.beans.BeansException;
11+
import org.springframework.beans.factory.BeanFactory;
12+
import org.springframework.beans.factory.BeanFactoryAware;
13+
import org.springframework.util.Assert;
14+
15+
/**
16+
* ApiBoot DataSource Switch
17+
*
18+
* @author:恒宇少年 - 于起宇
19+
* <p>
20+
* DateTime:2019-04-01 16:29
21+
* Blog:http://blog.yuqiyu.com
22+
* WebSite:http://www.jianshu.com/u/092df3f77bca
23+
* Gitee:https://gitee.com/hengboy
24+
* GitHub:https://github.com/hengboy
25+
*/
26+
public class ApiBootDataSourceSwitchAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
27+
28+
private Advice advice;
29+
private Pointcut pointcut;
30+
private BeanFactory beanFactory;
31+
32+
/**
33+
* init config
34+
*/
35+
public ApiBootDataSourceSwitchAdvisor(ApiBootDataSourceSwitchAnnotationInterceptor apiBootDataSourceSwitchAnnotationInterceptor) {
36+
// build pointcut instance
37+
this.pointcut = buildPointcut();
38+
// build advice instance
39+
this.advice = apiBootDataSourceSwitchAnnotationInterceptor;
40+
if (this.advice instanceof BeanFactoryAware) {
41+
((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
42+
}
43+
}
44+
45+
@Override
46+
public Pointcut getPointcut() {
47+
Assert.notNull(this.pointcut, "pointcut is required.");
48+
return this.pointcut;
49+
}
50+
51+
@Override
52+
public Advice getAdvice() {
53+
Assert.notNull(this.advice, "advice is required.");
54+
return this.advice;
55+
}
56+
57+
@Override
58+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
59+
this.beanFactory = beanFactory;
60+
}
61+
62+
/**
63+
* build pointcut instance
64+
*/
65+
private Pointcut buildPointcut() {
66+
// class
67+
Pointcut cpc = new AnnotationMatchingPointcut(DataSourceSwitch.class, true);
68+
// method
69+
Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DataSourceSwitch.class);
70+
71+
//union
72+
ComposablePointcut pointcut = new ComposablePointcut(cpc);
73+
return pointcut.union(mpc);
74+
}
75+
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.minbox.framework.api.boot.plugin.datasource.aop.interceptor;
2+
3+
import org.aopalliance.intercept.MethodInterceptor;
4+
import org.aopalliance.intercept.MethodInvocation;
5+
import org.minbox.framework.api.boot.plugin.datasource.annotation.DataSourceSwitch;
6+
import org.minbox.framework.api.boot.plugin.datasource.routing.DataSourceContextHolder;
7+
import org.springframework.aop.support.AopUtils;
8+
import org.springframework.core.BridgeMethodResolver;
9+
import org.springframework.util.ClassUtils;
10+
11+
import java.lang.reflect.Method;
12+
13+
/**
14+
* ApiBoot DataSource Advice
15+
* use spring aop
16+
*
17+
* @author:恒宇少年 - 于起宇
18+
* <p>
19+
* DateTime:2019-04-01 16:44
20+
* Blog:http://blog.yuqiyu.com
21+
* WebSite:http://www.jianshu.com/u/092df3f77bca
22+
* Gitee:https://gitee.com/hengboy
23+
* GitHub:https://github.com/hengboy
24+
*/
25+
public class ApiBootDataSourceSwitchAnnotationInterceptor implements MethodInterceptor {
26+
27+
@Override
28+
public Object invoke(MethodInvocation invocation) throws Throwable {
29+
try {
30+
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
31+
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
32+
Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
33+
// get class declared DataSourceSwitch annotation
34+
DataSourceSwitch dataSourceSwitch = targetClass.getDeclaredAnnotation(DataSourceSwitch.class);
35+
if (dataSourceSwitch == null) {
36+
// get declared DataSourceSwitch annotation
37+
dataSourceSwitch = userDeclaredMethod.getDeclaredAnnotation(DataSourceSwitch.class);
38+
}
39+
if (dataSourceSwitch != null) {
40+
// setting current thread use data source pool name
41+
DataSourceContextHolder.set(dataSourceSwitch.value());
42+
}
43+
return invocation.proceed();
44+
} finally {
45+
// remove current thread use datasource pool name
46+
DataSourceContextHolder.remove();
47+
}
48+
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.minbox.framework.api.boot.plugin.datasource.config;
2+
3+
import lombok.Data;
4+
5+
import javax.sql.DataSource;
6+
7+
/**
8+
* data source basic config parameter
9+
* for example:
10+
* username、url、password
11+
*
12+
* @author:恒宇少年 - 于起宇
13+
* <p>
14+
* DateTime:2019-04-01 11:41
15+
* Blog:http://blog.yuqiyu.com
16+
* WebSite:http://www.jianshu.com/u/092df3f77bca
17+
* Gitee:https://gitee.com/hengboy
18+
* GitHub:https://github.com/hengboy
19+
*/
20+
@Data
21+
public class DataSourceConfig {
22+
/**
23+
* data source pool name
24+
*/
25+
private String poolName = "master";
26+
/**
27+
* data source type
28+
*
29+
* @see org.minbox.framework.api.boot.plugin.datasource.DataSourceTypeNames
30+
*/
31+
private Class<? extends DataSource> dataSourceType;
32+
/**
33+
* driver class name
34+
* if don't config will use default values
35+
* MySQL8.0+ -> com.mysql.cj.jdbc.Driver(SpringBoot2.x recommended use)
36+
* MySQL8.0- -> com.mysql.jdbc.Driver
37+
*/
38+
private String driverClassName = "com.mysql.cj.jdbc.Driver";
39+
/**
40+
* database connection url
41+
*/
42+
private String url;
43+
/**
44+
* database connection username
45+
*/
46+
private String username;
47+
/**
48+
* database connection password
49+
*/
50+
private String password;
51+
}

0 commit comments

Comments
 (0)