Spring通過攔截器實(shí)現(xiàn)多數(shù)據(jù)源切換的示例代碼
在 Spring 應(yīng)用中,特別是使用了多數(shù)據(jù)源的場(chǎng)景下,可以通過**攔截器(Interceptor)**機(jī)制來實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換。Spring 提供了多種方式來管理多個(gè)數(shù)據(jù)源,并允許開發(fā)者根據(jù)業(yè)務(wù)邏輯的需求靈活地選擇不同的數(shù)據(jù)源進(jìn)行數(shù)據(jù)庫(kù)操作。
下面介紹如何通過攔截器實(shí)現(xiàn)多數(shù)據(jù)源切換的基本思路和具體步驟。
?? 一句話總結(jié):
? 通過自定義 HandlerInterceptor 或 MyBatis 的 Interceptor 攔截請(qǐng)求或 SQL 執(zhí)行過程,在合適的時(shí)機(jī)動(dòng)態(tài)設(shè)置當(dāng)前線程的數(shù)據(jù)源,從而實(shí)現(xiàn)基于請(qǐng)求或方法級(jí)別的多數(shù)據(jù)源切換。
?? 一、基本思路
- 配置多個(gè)數(shù)據(jù)源:首先需要在 Spring 配置類中定義多個(gè)
DataSource
Bean。 - 動(dòng)態(tài)數(shù)據(jù)源路由:創(chuàng)建一個(gè)代理數(shù)據(jù)源,它能夠根據(jù)某些條件(如當(dāng)前線程中的某個(gè)標(biāo)識(shí)符)選擇實(shí)際使用的數(shù)據(jù)源。
- 攔截請(qǐng)求或方法調(diào)用:使用 Spring MVC 的
HandlerInterceptor
或 MyBatis 的Interceptor
來攔截請(qǐng)求或方法調(diào)用,在執(zhí)行之前設(shè)置當(dāng)前線程的數(shù)據(jù)源標(biāo)識(shí)符。 - 清除上下文:確保在請(qǐng)求結(jié)束后清理線程局部變量,避免影響后續(xù)請(qǐng)求。
??? 二、具體實(shí)現(xiàn)步驟
1.定義多個(gè)數(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)建動(dò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()
方法返回當(dāng)前應(yīng)該使用的數(shù)據(jù)源標(biāo)識(shí)符。- 使用
ThreadLocal
變量存儲(chǔ)每個(gè)線程的數(shù)據(jù)源標(biāo)識(shí)符。
3.注冊(cè)動(dòng)態(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è)置默認(rèn)數(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ù)請(qǐng)求路徑或其他條件設(shè)置數(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
注解來實(shí)現(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 { // 假設(shè)有一個(gè)方法可以根據(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.注冊(cè)攔截器
a.Spring MVC 攔截器注冊(cè)
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private DataSourceInterceptor dataSourceInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(dataSourceInterceptor).addPathPatterns("/**"); } }
b.MyBatis 攔截器注冊(cè)
如果你使用的是 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(); } }
?? 三、注意事項(xiàng)
- 線程安全:由于我們使用了
ThreadLocal
來存儲(chǔ)數(shù)據(jù)源標(biāo)識(shí)符,因此在每次請(qǐng)求結(jié)束時(shí)都必須清理該變量,以防止內(nèi)存泄漏或跨請(qǐng)求污染。 - 事務(wù)管理:如果涉及到事務(wù),需確保事務(wù)與數(shù)據(jù)源綁定正確。通常情況下,Spring 的事務(wù)管理器會(huì)自動(dòng)處理這個(gè)問題,但如果你手動(dòng)管理事務(wù),可能需要額外注意。
- 性能考慮:頻繁地切換數(shù)據(jù)源可能會(huì)帶來一定的性能開銷,尤其是在高并發(fā)場(chǎng)景下。因此,應(yīng)盡量減少不必要的切換頻率。
- 異常處理:確保在發(fā)生異常時(shí)也能正確地清理上下文,避免潛在的問題。
到此這篇關(guān)于Spring通過攔截器實(shí)現(xiàn)多數(shù)據(jù)源切換的示例代碼的文章就介紹到這了,更多相關(guān)Spring 攔截器多數(shù)據(jù)源切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Boot 動(dòng)態(tài)數(shù)據(jù)源示例(多數(shù)據(jù)源自動(dòng)切換)
- Springboot如何設(shè)置多數(shù)據(jù)源,隨時(shí)切換
- SpringBoot AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換的方法
- Spring實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源的解決方案
- spring boot+mybatis 多數(shù)據(jù)源切換(實(shí)例講解)
- Spring配置多數(shù)據(jù)源切換
- Spring整合多數(shù)據(jù)源實(shí)現(xiàn)動(dòng)態(tài)切換的實(shí)例講解
- 詳解Spring多數(shù)據(jù)源如何切換
相關(guān)文章
java并發(fā)高的情況下用ThreadLocalRandom來生成隨機(jī)數(shù)
如果我們想要生成一個(gè)隨機(jī)數(shù),通常會(huì)使用Random類。但是在并發(fā)情況下Random生成隨機(jī)數(shù)的性能并不是很理想,本文主要介紹了java并發(fā)高的情況下用ThreadLocalRandom來生成隨機(jī)數(shù),感興趣的可以了解一下2022-05-05基于springboot攔截器HandlerInterceptor的注入問題
這篇文章主要介紹了springboot攔截器HandlerInterceptor的注入問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09SpringCloud Alibaba Seata (收藏版)
Seata是一款開源的分布式事務(wù)解決方案,致力于在微服務(wù)架構(gòu)在提供高性能和簡(jiǎn)單一樣的分布式事務(wù)服務(wù)。這篇文章主要介紹了SpringCloud Alibaba Seata 的相關(guān)知識(shí),需要的朋友可以參考下2020-10-10eclipse導(dǎo)入工程報(bào)錯(cuò)問題項(xiàng)目或者文件有紅叉的解決方案
這篇文章主要介紹了eclipse導(dǎo)入工程報(bào)錯(cuò)問題項(xiàng)目或者文件有紅叉的解決方案,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05在Spring Data JPA中引入Querydsl的實(shí)現(xiàn)方式
這篇文章主要介紹了在Spring Data JPA中引入Querydsl的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01Spring?Boot中JSON數(shù)值溢出問題從報(bào)錯(cuò)到優(yōu)雅解決辦法
這篇文章主要介紹了Spring?Boot中JSON數(shù)值溢出問題從報(bào)錯(cuò)到優(yōu)雅的解決辦法,通過修改字段類型為L(zhǎng)ong、添加全局異常處理和數(shù)據(jù)校驗(yàn),解決了該問題,文章還提供了類型范圍推薦場(chǎng)景和常見問題解答,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-04-04spring boot 測(cè)試單元修改數(shù)據(jù)庫(kù)不成功的解決
這篇文章主要介紹了spring boot 測(cè)試單元修改數(shù)據(jù)庫(kù)不成功的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09深入解析Java中的Classloader的運(yùn)行機(jī)制
這篇文章主要介紹了Java中的Classloader的運(yùn)行機(jī)制,包括從JVM方面講解類加載器的委托機(jī)制等,需要的朋友可以參考下2015-11-11Springboot如何根據(jù)docx填充生成word文件并導(dǎo)出pdf
這篇文章主要介紹了Springboot如何根據(jù)docx填充生成word文件并導(dǎo)出pdf問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08