Spring通過攔截器實現(xiàn)多數(shù)據(jù)源切換的示例代碼
在 Spring 應用中,特別是使用了多數(shù)據(jù)源的場景下,可以通過**攔截器(Interceptor)**機制來實現(xiàn)動態(tài)數(shù)據(jù)源切換。Spring 提供了多種方式來管理多個數(shù)據(jù)源,并允許開發(fā)者根據(jù)業(yè)務邏輯的需求靈活地選擇不同的數(shù)據(jù)源進行數(shù)據(jù)庫操作。
下面介紹如何通過攔截器實現(xiàn)多數(shù)據(jù)源切換的基本思路和具體步驟。
?? 一句話總結(jié):
? 通過自定義 HandlerInterceptor 或 MyBatis 的 Interceptor 攔截請求或 SQL 執(zhí)行過程,在合適的時機動態(tài)設置當前線程的數(shù)據(jù)源,從而實現(xiàn)基于請求或方法級別的多數(shù)據(jù)源切換。
?? 一、基本思路
- 配置多個數(shù)據(jù)源:首先需要在 Spring 配置類中定義多個
DataSourceBean。 - 動態(tài)數(shù)據(jù)源路由:創(chuàng)建一個代理數(shù)據(jù)源,它能夠根據(jù)某些條件(如當前線程中的某個標識符)選擇實際使用的數(shù)據(jù)源。
- 攔截請求或方法調(diào)用:使用 Spring MVC 的
HandlerInterceptor或 MyBatis 的Interceptor來攔截請求或方法調(diào)用,在執(zhí)行之前設置當前線程的數(shù)據(jù)源標識符。 - 清除上下文:確保在請求結(jié)束后清理線程局部變量,避免影響后續(xù)請求。
??? 二、具體實現(xiàn)步驟
1.定義多個數(shù)據(jù)源
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}2.創(chuàng)建動態(tài)數(shù)據(jù)源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
@Override
protected Object determineCurrentLookupKey() {
return contextHolder.get();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}determineCurrentLookupKey()方法返回當前應該使用的數(shù)據(jù)源標識符。- 使用
ThreadLocal變量存儲每個線程的數(shù)據(jù)源標識符。
3.注冊動態(tài)數(shù)據(jù)源
@Configuration
public class DynamicDataSourceConfig {
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("primary", primaryDataSource);
targetDataSources.put("secondary", secondaryDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource); // 設置默認數(shù)據(jù)源
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
}4.編寫攔截器
a.Spring MVC 攔截器
@Component
public class DataSourceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 根據(jù)請求路徑或其他條件設置數(shù)據(jù)源
if (request.getRequestURI().startsWith("/api/secondary")) {
DynamicDataSource.setDataSource("secondary");
} else {
DynamicDataSource.setDataSource("primary");
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
DynamicDataSource.clearDataSource(); // 清理線程局部變量
}
}b.MyBatis 攔截器
如果你想要基于 MyBatis 的 @Intercepts 注解來實現(xiàn),則可以這樣做:
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class DataSourceInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
// 假設有一個方法可以根據(jù)某些條件確定要使用的數(shù)據(jù)源
String dataSourceKey = determineDataSourceKey(invocation.getMethod().getName());
DynamicDataSource.setDataSource(dataSourceKey);
return invocation.proceed();
} finally {
DynamicDataSource.clearDataSource();
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}5.注冊攔截器
a.Spring MVC 攔截器注冊
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DataSourceInterceptor dataSourceInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(dataSourceInterceptor).addPathPatterns("/**");
}
}b.MyBatis 攔截器注冊
如果你使用的是 MyBatis 攔截器,可以在配置類中添加如下代碼:
@Configuration
public class MyBatisConfig {
@Autowired
private DataSourceInterceptor myBatisDataSourceInterceptor;
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setPlugins(new Interceptor[]{myBatisDataSourceInterceptor});
return sessionFactory.getObject();
}
}?? 三、注意事項
- 線程安全:由于我們使用了
ThreadLocal來存儲數(shù)據(jù)源標識符,因此在每次請求結(jié)束時都必須清理該變量,以防止內(nèi)存泄漏或跨請求污染。 - 事務管理:如果涉及到事務,需確保事務與數(shù)據(jù)源綁定正確。通常情況下,Spring 的事務管理器會自動處理這個問題,但如果你手動管理事務,可能需要額外注意。
- 性能考慮:頻繁地切換數(shù)據(jù)源可能會帶來一定的性能開銷,尤其是在高并發(fā)場景下。因此,應盡量減少不必要的切換頻率。
- 異常處理:確保在發(fā)生異常時也能正確地清理上下文,避免潛在的問題。
到此這篇關于Spring通過攔截器實現(xiàn)多數(shù)據(jù)源切換的示例代碼的文章就介紹到這了,更多相關Spring 攔截器多數(shù)據(jù)源切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot整合MongoDB實現(xiàn)文件上傳下載刪除
這篇文章主要介紹了SpringBoot整合MongoDB實現(xiàn)文件上傳下載刪除的方法,幫助大家更好的理解和學習使用SpringBoot框架,感興趣的朋友可以了解下2021-05-05
集群環(huán)境中使用ehcache_動力節(jié)點Java學院整理
這篇文章主要為大家詳細介紹了集群環(huán)境中使用ehcache的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Java內(nèi)存泄漏問題處理方法經(jīng)驗總結(jié)
今天小編就為大家分享一篇關于Java內(nèi)存泄漏問題處理方法經(jīng)驗總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01

