Spring-Boot + AOP實現多數據源動態切換

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

最近在做保證金余額查詢優化,在項目啟動時候需要把余額全量加載到本地緩存,因為需要全量查詢所有騎手的保證金余額,為了不影響主數據庫的性能,考慮把這個查詢走從庫。所以涉及到需要在一個項目中配置多數據源,并且能夠動態切換。

設計總體思路

Spring-Boot+AOP方式實現多數據源切換,繼承AbstractRoutingDataSource實現數據源動態的獲取,在service層使用注解指定數據源。

步驟

一、多數據源配置

在application.properties中,我們的配置是這樣的


#主數據源druid.master.url=jdbc:mysql://url/masterdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNulldruid.master.username=xxxdruid.master.password=123druid.master.driver-class-name=com.mysql.jdbc.Driverdruid.master.max-wait=5000druid.master.max-active=100druid.master.test-on-borrow=truedruid.master.validation-query=SELECT 1#從數據源druid.slave.url=jdbc:mysql://url/slavedb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNulldruid.slave.username=xxxdruid.slave.password=123druid.slave.driver-class-name=com.mysql.jdbc.Driverdruid.slave.max-wait=5000druid.slave.max-active=100druid.slave.test-on-borrow=truedruid.slave.validation-query=SELECT 1

讀取配置

	<!-- master數據源 --><bean primary="true" id="masterdb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!-- 基本屬性 url、user、password --><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="${druid.master.url}"/><property name="username" value="${druid.master.username}"/><property name="password" value="${druid.master.password}"/><!-- 配置初始化最大 --><property name="maxActive" value="${druid.master.max-active}"/><!-- 配置獲取連接等待超時的時間 --><property name="maxWait" value="${druid.master.max-wait}"/><property name="validationQuery" value="${druid.master.validation-query}"/><property name="testOnBorrow" value="${druid.master.test-on-borrow}"/></bean><!-- slave數據源 --><bean primary="true" id="slavedb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><!-- 基本屬性 url、user、password --><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="${druid.slave.url}"/><property name="username" value="${druid.slave.username}"/><property name="password" value="${druid.slave.password}"/><!-- 配置初始化大小、最小、最大 --><property name="maxActive" value="${druid.slave.max-active}"/><!-- 配置獲取連接等待超時的時間 --><property name="maxWait" value="${druid.slave.max-wait}"/><property name="validationQuery" value="${druid.slave.validation-query}"/><property name="testOnBorrow" value="${druid.slave.test-on-borrow}"/></bean><!-- 動態數據源,根據service接口上的注解來決定取哪個數據源 --><bean id="dataSource" class="datasource.DynamicDataSource"><property name="targetDataSources"><map key-type="java.lang.String"><entry key="slave" value-ref="slavedb"/><entry key="master" value-ref="masterdb"/></map></property><property name="defaultTargetDataSource" ref="masterdb"/></bean><!-- Spring JdbcTemplate --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean><!-- Spring事務管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"><property name="transactionManager" ref="transactionManager"/></bean><tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="2" /><!-- depositdbSqlSessionFactory --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="mapperLocations" value="classpath*:mapper-xxdb/*Mapper*.xml" /></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="xxdb.mapper"/><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/></bean>

二. 數據源動態切換類

動態數據源切換是基于AOP的,所以我們需要聲明一個AOP切面,并在切面前做數據源切換,切面完成后移除數據源名稱。

	@Order(1)   //設置AOP執行順序(需要在事務之前,否則事務只發生在默認庫中)@Aspect@Componentpublic class DataSourceAspect {private Logger logger = LoggerFactory.getLogger(this.getClass());//切點@Pointcut("execution(* com.xxx.service.*.*(..))")public void aspect() { }@Before("aspect()")private void before(JoinPoint point) {Object target = point.getTarget();String method = point.getSignature().getName();Class<?> classz = target.getClass();// 獲取目標類Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();try {Method m = classz.getMethod(method, parameterTypes);if (m != null && m.isAnnotationPresent(MyDataSource.class)) {MyDataSource data = m.getAnnotation(MyDataSource.class);logger.info("method :{},datasource:{}",m.getName() ,data.value().getName());JdbcContextHolder.putDataSource(data.value().getName());// 數據源放到當前線程中}} catch (Exception e) {logger.error("get datasource error ",e);//默認選擇masterJdbcContextHolder.putDataSource(DataSourceType.Master.getName());// 數據源放到當前線程中}}@AfterReturning("aspect()")public void after(JoinPoint point) {JdbcContextHolder.clearDataSource();}}

三、數據源管理類

	public class JdbcContextHolder {private final static ThreadLocal<String> local = new ThreadLocal<>();public static void putDataSource(String name) {local.set(name);}public static String getDataSource() {return local.get();}public static void clearDataSource() {local.remove();}}

四、動態數據源

spring為我們提供了AbstractRoutingDataSource,即帶路由的數據源。繼承后我們需要實現它的determineCurrentLookupKey(),該方法用于自定義實際數據源名稱的路由選擇方法,由于我們將信息保存到了ThreadLocal中,所以只需要從中拿出來即可。

	public class DynamicDataSource extends AbstractRoutingDataSource {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overrideprotected Object determineCurrentLookupKey() {String dataSource = JdbcContextHolder.getDataSource();logger.info("數據源為{}",dataSource);return dataSource;}}

五、數據源注解和枚舉

我們切換數據源時,一般都是在調用具體接口的方法前實現,所以我們定義一個方法注解,當AOP檢測到方法上有該注解時,根據注解中value對應的名稱進行切換。

	@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyDataSource {DataSourceType value();}

	public enum  DataSourceType {// 主表Master("master"),// 從表Slave("slave");private String name;private DataSourceType(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}

六、切點注解

由于我們的動態數據源配置了默認庫,所以如果方法是操作默認庫的可以不需要注解,如果要操作非默認數據源,我們需要在方法上添加@MyDataSource("數據源名稱")注解,這樣就可以利用AOP實現動態切換了

	@Componentpublic class xxxServiceImpl {@Resourceprivate XxxMapperExt xxxMapperExt;@MyDataSource(value= DataSourceType.Slave)public List<Object> getAll(){return xxxMapperExt.getAll();}}

轉載于:https://my.oschina.net/stephenzhang/blog/1786156

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/390862.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390862.shtml
英文地址,請注明出處:http://en.pswp.cn/news/390862.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

css 幻燈片_如何使用HTML,CSS和JavaScript創建幻燈片

css 幻燈片A web slideshow is a sequence of images or text that consists of showing one element of the sequence in a certain time interval.網絡幻燈片是一系列圖像或文本&#xff0c;包括在一定時間間隔內顯示序列中的一個元素。 For this tutorial you can create a…

leetcode 1738. 找出第 K 大的異或坐標值

本文正在參加「Java主題月 - Java 刷題打卡」&#xff0c;詳情查看 活動鏈接 題目 給你一個二維矩陣 matrix 和一個整數 k &#xff0c;矩陣大小為 m x n 由非負整數組成。 矩陣中坐標 (a, b) 的 值 可由對所有滿足 0 < i < a < m 且 0 < j < b < n 的元素…

【數據庫】Oracle用戶、授權、角色管理

創建和刪除用戶是Oracle用戶管理中的常見操作&#xff0c;但這其中隱含了Oracle數據庫系統的系統權限與對象權限方面的知識。掌握還Oracle用戶的授權操作和原理&#xff0c;可以有效提升我們的工作效率。 Oracle數據庫的權限系統分為系統權限與對象權限。系統權限( Database Sy…

商業數據科學

數據科學 &#xff0c; 意見 (Data Science, Opinion) “There is a saying, ‘A jack of all trades and a master of none.’ When it comes to being a data scientist you need to be a bit like this, but perhaps a better saying would be, ‘A jack of all trades and …

為什么游戲開發者不玩游戲_什么是游戲開發?

為什么游戲開發者不玩游戲Game Development is the art of creating games and describes the design, development and release of a game. It may involve concept generation, design, build, test and release. While you create a game, it is important to think about t…

leetcode 692. 前K個高頻單詞

題目 給一非空的單詞列表&#xff0c;返回前 k 個出現次數最多的單詞。 返回的答案應該按單詞出現頻率由高到低排序。如果不同的單詞有相同出現頻率&#xff0c;按字母順序排序。 示例 1&#xff1a; 輸入: ["i", "love", "leetcode", "…

數據顯示,中國近一半的獨角獸企業由“BATJ”四巨頭投資

中國的互聯網行業越來越有被巨頭壟斷的趨勢。百度、阿里巴巴、騰訊、京東&#xff0c;這四大巨頭支撐起了中國近一半的獨角獸企業。CB Insights日前發表了題為“Nearly Half Of China’s Unicorns Backed By Baidu, Alibaba, Tencent, Or JD.com”的數據分析文章&#xff0c;列…

Java的Servlet、Filter、Interceptor、Listener

寫在前面&#xff1a; 使用Spring-Boot時&#xff0c;嵌入式Servlet容器可以通過掃描注解&#xff08;ServletComponentScan&#xff09;的方式注冊Servlet、Filter和Servlet規范的所有監聽器&#xff08;如HttpSessionListener監聽器&#xff09;。 Spring boot 的主 Servlet…

html5教程_最好HTML和HTML5教程

html5教程HyperText Markup Language (HTML) is a markup language used to construct online documents and is the foundation of most websites today. A markup language like HTML allows us to超文本標記語言(HTML)是用于構造在線文檔的標記語言&#xff0c;并且是當今大…

leetcode 1035. 不相交的線(dp)

在兩條獨立的水平線上按給定的順序寫下 nums1 和 nums2 中的整數。 現在&#xff0c;可以繪制一些連接兩個數字 nums1[i] 和 nums2[j] 的直線&#xff0c;這些直線需要同時滿足滿足&#xff1a; nums1[i] nums2[j] 且繪制的直線不與任何其他連線&#xff08;非水平線&#x…

SPI和RAM IP核

學習目的&#xff1a; &#xff08;1&#xff09; 熟悉SPI接口和它的讀寫時序&#xff1b; &#xff08;2&#xff09; 復習Verilog仿真語句中的$readmemb命令和$display命令&#xff1b; &#xff08;3&#xff09; 掌握SPI接口寫時序操作的硬件語言描述流程&#xff08;本例僅…

個人技術博客Alpha----Android Studio UI學習

項目聯系 這次的項目我在前端組&#xff0c;負責UI&#xff0c;下面簡略講下學到的內容和使用AS過程中遇到的一些問題及其解決方法。 常見UI控件的使用 1.TextView 在TextView中&#xff0c;首先用android:id給當前控件定義一個唯一標識符。在活動中通過這個標識符對控件進行事…

數據科學家數據分析師_站出來! 分析人員,數據科學家和其他所有人的領導和溝通技巧...

數據科學家數據分析師這一切如何發生&#xff1f; (How did this All Happen?) As I reflect on my life over the past few years, even though I worked my butt off to get into Data Science as a Product Analyst, I sometimes still find myself begging the question, …

leetcode 810. 黑板異或游戲

黑板上寫著一個非負整數數組 nums[i] 。Alice 和 Bob 輪流從黑板上擦掉一個數字&#xff0c;Alice 先手。如果擦除一個數字后&#xff0c;剩余的所有數字按位異或運算得出的結果等于 0 的話&#xff0c;當前玩家游戲失敗。 (另外&#xff0c;如果只剩一個數字&#xff0c;按位異…

react-hooks_在5分鐘內學習React Hooks-初學者教程

react-hooksSometimes 5 minutes is all youve got. So in this article, were just going to touch on two of the most used hooks in React: useState and useEffect. 有時只有5分鐘。 因此&#xff0c;在本文中&#xff0c;我們僅涉及React中兩個最常用的鉤子&#xff1a; …

分析工作試用期收獲_免費使用零編碼技能探索數據分析

分析工作試用期收獲Have you been hearing the new industry buzzword — Data Analytics(it was AI-ML earlier) a lot lately? Does it sound complicated and yet simple enough? Understand the logic behind models but dont know how to code? Apprehensive of spendi…

select的一些問題。

這個要怎么統計類別數呢&#xff1f; 哇哇哇 解決了。 之前怎么沒想到呢&#xff1f;感謝一樓。轉載于:https://www.cnblogs.com/AbsolutelyPerfect/p/7818701.html

html5語義化標記元素_語義HTML5元素介紹

html5語義化標記元素Semantic HTML elements are those that clearly describe their meaning in a human- and machine-readable way. 語義HTML元素是以人類和機器可讀的方式清楚地描述其含義的元素。 Elements such as <header>, <footer> and <article> …

重學TCP協議(12)SO_REUSEADDR、SO_REUSEPORT、SO_LINGER

1. SO_REUSEADDR 假如服務端出現故障&#xff0c;主動斷開連接以后&#xff0c;需要等 2 個 MSL 以后才最終釋放這個連接&#xff0c;而服務重啟以后要綁定同一個端口&#xff0c;默認情況下&#xff0c;操作系統的實現都會阻止新的監聽套接字綁定到這個端口上。啟用 SO_REUSE…

殘疾科學家_數據科學與殘疾:通過創新加強護理

殘疾科學家Could the time it takes for you to water your houseplants say something about your health? Or might the amount you’re moving around your neighborhood reflect your mental health status?您給植物澆水所需的時間能否說明您的健康狀況&#xff1f; 還是…