詳細(xì)聊聊SpringBoot中動態(tài)切換數(shù)據(jù)源的方法
其實這個表示有點不太對,應(yīng)該是 Druid 動態(tài)切換數(shù)據(jù)源的方法,只是應(yīng)用在了 springboot 框架中,準(zhǔn)備代碼準(zhǔn)備了半天,之前在一次數(shù)據(jù)庫遷移中使用了,發(fā)現(xiàn) Druid 還是很強(qiáng)大的,用來做動態(tài)數(shù)據(jù)源切換很方便。
首先這里的場景跟我原來用的有點點區(qū)別,在項目中使用的是通過配置中心控制數(shù)據(jù)源切換,統(tǒng)一切換,而這里的例子多加了個可以根據(jù)接口注解配置
第一部分是最核心的,如何基于 Spring JDBC 和 Druid 來實現(xiàn)數(shù)據(jù)源切換,是繼承了org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 這個類,他的determineCurrentLookupKey方法會被調(diào)用來獲得用來決定選擇那個數(shù)據(jù)源的對象,也就是 lookupKey,也可以通過這個類看到就是通過這個 lookupKey 來路由找到數(shù)據(jù)源。
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { if (DatabaseContextHolder.getDatabaseType() != null) { return DatabaseContextHolder.getDatabaseType().getName(); } return DatabaseType.MASTER1.getName(); } }
而如何使用這個 lookupKey 呢,就涉及到我們的 DataSource 配置了,原來就是我們可以直接通過spring 的 jdbc 配置數(shù)據(jù)源,像這樣
現(xiàn)在我們要使用 Druid 作為數(shù)據(jù)源了,然后配置 DynamicDataSource的參數(shù),通過 key 來選擇對應(yīng)的 DataSource,也就是下面配的 master1 和 master2
<bean id="master1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" p:driverClassName="com.mysql.cj.jdbc.Driver" p:url="${master1.demo.datasource.url}" p:username="${master1.demo.datasource.username}" p:password="${master1.demo.datasource.password}" p:initialSize="5" p:minIdle="1" p:maxActive="10" p:maxWait="60000" p:timeBetweenEvictionRunsMillis="60000" p:minEvictableIdleTimeMillis="300000" p:validationQuery="SELECT 'x'" p:testWhileIdle="true" p:testOnBorrow="false" p:testOnReturn="false" p:poolPreparedStatements="false" p:maxPoolPreparedStatementPerConnectionSize="20" p:connectionProperties="config.decrypt=true" p:filters="stat,config"/> <bean id="master2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" p:driverClassName="com.mysql.cj.jdbc.Driver" p:url="${master2.demo.datasource.url}" p:username="${master2.demo.datasource.username}" p:password="${master2.demo.datasource.password}" p:initialSize="5" p:minIdle="1" p:maxActive="10" p:maxWait="60000" p:timeBetweenEvictionRunsMillis="60000" p:minEvictableIdleTimeMillis="300000" p:validationQuery="SELECT 'x'" p:testWhileIdle="true" p:testOnBorrow="false" p:testOnReturn="false" p:poolPreparedStatements="false" p:maxPoolPreparedStatementPerConnectionSize="20" p:connectionProperties="config.decrypt=true" p:filters="stat,config"/> <bean id="dataSource" class="com.nicksxs.springdemo.config.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- master --> <entry key="master1" value-ref="master1"/> <!-- slave --> <entry key="master2" value-ref="master2"/> </map> </property> <property name="defaultTargetDataSource" ref="master1"/> </bean>
現(xiàn)在就要回到頭上,介紹下這個DatabaseContextHolder,這里使用了 ThreadLocal 存放這個 DatabaseType,為啥要用這個是因為前面說的我們想要讓接口層面去配置不同的數(shù)據(jù)源,要把持相互隔離不受影響,就使用了 ThreadLocal,關(guān)于它也可以看我前面寫的一篇文章聊聊傳說中的 ThreadLocal,而 DatabaseType 就是個簡單的枚舉
public class DatabaseContextHolder { public static final ThreadLocal<DatabaseType> databaseTypeThreadLocal = new ThreadLocal<>(); public static DatabaseType getDatabaseType() { return databaseTypeThreadLocal.get(); } public static void putDatabaseType(DatabaseType databaseType) { databaseTypeThreadLocal.set(databaseType); } public static void clearDatabaseType() { databaseTypeThreadLocal.remove(); } } public enum DatabaseType { MASTER1("master1", "1"), MASTER2("master2", "2"); private final String name; private final String value; DatabaseType(String name, String value) { this.name = name; this.value = value; } public String getName() { return name; } public String getValue() { return value; } public static DatabaseType getDatabaseType(String name) { if (MASTER2.name.equals(name)) { return MASTER2; } return MASTER1; } }
這邊可以看到就是通過動態(tài)地通過putDatabaseType設(shè)置lookupKey來進(jìn)行數(shù)據(jù)源切換,要通過接口注解配置來進(jìn)行設(shè)置的話,我們就需要一個注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { String value(); }
這個注解可以配置在我的接口方法上,比如這樣
public interface StudentService { @DataSource("master1") public Student queryOne(); @DataSource("master2") public Student queryAnother(); }
通過切面來進(jìn)行數(shù)據(jù)源的設(shè)置
@Aspect @Component @Order(-1) public class DataSourceAspect { @Pointcut("execution(* com.nicksxs.springdemo.service..*.*(..))") public void pointCut() { } @Before("pointCut()") public void before(JoinPoint point) { Object target = point.getTarget(); System.out.println(target.toString()); String method = point.getSignature().getName(); System.out.println(method); Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); try { Method m = classz[0].getMethod(method, parameterTypes); System.out.println("method"+ m.getName()); if (m.isAnnotationPresent(DataSource.class)) { DataSource data = m.getAnnotation(DataSource.class); System.out.println("dataSource:"+data.value()); DatabaseContextHolder.putDatabaseType(DatabaseType.getDatabaseType(data.value())); } } catch (Exception e) { e.printStackTrace(); } } @After("pointCut()") public void after() { DatabaseContextHolder.clearDatabaseType(); } }
通過接口判斷是否帶有注解跟是注解的值,DatabaseType 的配置不太好,不過先忽略了,然后在切點后進(jìn)行清理
這是我 master1 的數(shù)據(jù),
master2 的數(shù)據(jù)
然后跑一下簡單的 demo,
@Override public void run(String...args) { LOGGER.info("run here"); System.out.println(studentService.queryOne()); System.out.println(studentService.queryAnother()); }
看一下運行結(jié)果
其實這個方法應(yīng)用場景不止可以用來遷移數(shù)據(jù)庫,還能實現(xiàn)精細(xì)化的讀寫數(shù)據(jù)源分離之類的,算是做個簡單記錄和分享。
總結(jié)
到此這篇關(guān)于SpringBoot中動態(tài)切換數(shù)據(jù)源的文章就介紹到這了,更多相關(guān)SpringBoot動態(tài)切換數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot集成@DS注解實現(xiàn)數(shù)據(jù)源切換的方法示例
- springboot+dynamicDataSource動態(tài)添加切換數(shù)據(jù)源方式
- SpringBoot?+DynamicDataSource切換多數(shù)據(jù)源的全過程
- Springboot實現(xiàn)根據(jù)用戶ID切換動態(tài)數(shù)據(jù)源
- Springboot動態(tài)切換數(shù)據(jù)源的具體實現(xiàn)與原理分析
- Spring配置多數(shù)據(jù)源切換
- SpringBoot AOP方式實現(xiàn)多數(shù)據(jù)源切換的方法
- Spring配置多個數(shù)據(jù)源并實現(xiàn)數(shù)據(jù)源的動態(tài)切換功能
相關(guān)文章
java對象序列化與反序列化的默認(rèn)格式和json格式使用示例
這篇文章主要介紹了java對象序列化與反序列化的默認(rèn)格式和json格式使用示例,需要的朋友可以參考下2014-02-02