動態切換資料來源原理
動態切換資料來源原理
有一種場景,呼叫ServiceA時,使用A資料來源,呼叫ServiceB時,使用B資料來源。在不改變原有程式碼的情況下,這個如何來實現呢。
下面是一種實現方式,供參考:
- 將各個資料來源用key-value形式儲存到map中,如下面這個類:
class RouteDatasource { Map<String, Datasource> map = new HashMap<>(); map.push("goods", goodsDatasource); map.push("order", orderDatasource); 獲取Connection public Connection getConnection() { ... } }
- 在操作某個Service類的時候選擇對應的資料來源
比如在操作com.project.service.goods.GoodsService.java時,key應該設定成goods。這裡有個問題是,service類跟資料來源是分開的,怎麼樣才能通知到
RouteDatasource類使用goods的key呢。
方法簡單,使用ThreadLocal
類,在使用GoodsService類時,ThreadLocal中放入key,然後RouteDatasource類從ThreadLocal中取。
class RouteDatasource { Map<String, Datasource> map... map.push("goods", goodsDatasource); map.push("order", orderDatasource); 獲取Connection public Connection getConnection() { String key = ThreadLocal.get(); Datasource ds = map.get(kay); return ds.getConnection(); } }
獲取key完成了,如何設定key呢?不可能在每次使用前put一下吧。容易出錯,程式碼也不美觀。有一種方案是使用spring的AOP方式實現。
AOP可以在執行某個Service方法前做一些操作,那麼我們就把設定key的操作放在AOP的before行為中。
大致思路是:判斷當前方法所在類的package的名稱來區分需要設定哪個key。
比如:com.project.service.goods.GoodsService.save()方法的package,是com.project.service.goods開頭
那麼AOP程式碼可以這樣寫
String methodInfo = ... if(methodInfo.startWith("com.project.service.goods")) { ThreadLocal.set("goods") }
完整的程式碼如下:
@Component @Aspect public class DynamicDataSourceAspect { @Before("execution(* com.project.service..*.*(..))") public void before(JoinPoint jp) { String methodName = jp.getSignature().getDeclaringTypeName(); if (StringUtils.isBlank(methodName)) { return; } if (methodName.startsWith("com.project.service.goods")) { ThreadLocal.set("goods") } else if (methodName.startsWith("com.project.service.order")) { ThreadLocal.set("order") } } }
這樣,以後只需要配置DynamicDataSourceAspect類即可。
資料來源動態切換,主要思想是通過事先定義好的Key-Value進行動態獲取。主要使用了ThreadLocal和Spring AOP方式去串連起來。
spring為我們提供了資料來源切換類org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
,這個類的作用就是一開始提到的RouteDatasource類。感興趣的同學可以看下程式碼,很簡單。