詳解Spring AOP 實(shí)現(xiàn)主從讀寫(xiě)分離
深刻討論為什么要讀寫(xiě)分離?
為了服務(wù)器承載更多的用戶?提升了網(wǎng)站的響應(yīng)速度?分?jǐn)倲?shù)據(jù)庫(kù)服務(wù)器的壓力?就是為了雙機(jī)熱備又不想浪費(fèi)備份服務(wù)器?上面這些回答,我認(rèn)為都不是錯(cuò)誤的,但也都不是完全正確的。「讀寫(xiě)分離」并不是多么神奇的東西,也帶不來(lái)多么大的性能提升,也許更多的作用的就是數(shù)據(jù)安全的備份吧。
從一個(gè)庫(kù)到讀寫(xiě)分離,從理論上對(duì)服務(wù)器壓力來(lái)說(shuō)是會(huì)帶來(lái)一倍的性能提升,但你仔細(xì)思考一下,你的應(yīng)用服務(wù)器真的很需要這一倍的提升么?那倒不如你去試著在服務(wù)器使用一下緩存系統(tǒng),如 Memcached、Redis 這些分布式緩存,那性能可能是幾十倍的提升。而且,在服務(wù)器硬件異常強(qiáng)悍及性能廉價(jià)的今天,完全更沒(méi)必要了,所以,在今天,我認(rèn)為它更多的職責(zé)就是為了數(shù)據(jù)安全而設(shè)計(jì)的,同時(shí)又提升了一些性能,這樣也挺好。
可能我們更應(yīng)該稱(chēng)之為主從分離。
利用 AOP 實(shí)現(xiàn)讀寫(xiě)分離
讀寫(xiě)分離方式很簡(jiǎn)單,就是在你讀數(shù)據(jù)是去連接從庫(kù),在你寫(xiě)數(shù)據(jù)的時(shí)候去連接主庫(kù),具體代碼實(shí)現(xiàn)當(dāng)然就是連接時(shí)候去操作了,這沒(méi)什么難度,在代碼里寫(xiě)就是了??墒?,有追求的程序猿都是不是這么解決問(wèn)題的呢!
其實(shí)通過(guò)上篇的 Spring AOP 攔截器的基本實(shí)現(xiàn) 我們知道 AOP 可以實(shí)現(xiàn)在方法開(kāi)始執(zhí)行前后插入執(zhí)行我們想要的代碼,那這樣,我們是不是可以在執(zhí)行數(shù)據(jù)庫(kù)操作前根據(jù)業(yè)務(wù)來(lái)動(dòng)態(tài)切換數(shù)據(jù)源呢?
思考一下這個(gè)方式理論上好像是可行的,這種方式首先不需要在業(yè)務(wù)代碼中去做切換,二是可能以后我們不需要讀寫(xiě)分離了,把 AOP 切換的代碼去掉就行了,三是可能就是拓展性好了。
等不了了,開(kāi)始擼代碼
你可能想深入的了解的話,我這里給你幾個(gè)程序里用到的關(guān)鍵字enum(枚舉)、annotation(自定義注解)、JoinPoint(注入點(diǎn))、AbstractRoutingDataSource(數(shù)據(jù)源接口子類(lèi)),你理解了這些就知道了,其實(shí)你并不需要深入某些深層的東西,了解下即可。
一、建立JdbcContextHolder.java類(lèi)
public class JdbcContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setJdbcType(String jdbcType) {
contextHolder.set(jdbcType);
}
public static void setSlave() {
setJdbcType("slave");
}
public static void setMaster() {
clearJdbcType();
}
public static String getJdbcType() {
return (String) contextHolder.get();
}
public static void clearJdbcType() {
contextHolder.remove();
}
}
這個(gè)類(lèi)的作用就是用來(lái)設(shè)置、獲取數(shù)據(jù)源連接
二、新建DynamicDataSource.java類(lèi),繼承于AbstractRoutingDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import cn.mayongfa.common.JdbcContextHolder;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 獲取當(dāng)前數(shù)據(jù)源連接
return JdbcContextHolder.getJdbcType();
}
}
通過(guò)研究,我們知道determineCurrentLookupKey方法是獲取相關(guān)數(shù)據(jù)源連接的,所以重寫(xiě)determineCurrentLookupKey方法就可以啦,然后我們?nèi)ネㄟ^(guò)剛剛我們建立的JdbcContextHolder類(lèi)去獲取。那怎么設(shè)置呢?
三、建立數(shù)據(jù)源DataSourceType.java枚舉類(lèi)
public enum DataSourceType {
//主庫(kù)
Master("master"),
//從庫(kù)
Slave("slave");
private DataSourceType(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
這個(gè)枚舉類(lèi)的作用其實(shí)就是為了設(shè)置數(shù)據(jù)源而生的,它的目的就是讓設(shè)置數(shù)據(jù)源時(shí)更方便,如絲般順滑。
四、新建DataSource.java Annotation(自定義注解)類(lèi)
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataSource {
DataSourceType value() default DataSourceType.Master;
}
自定義注解的意義不再過(guò)多討論,一句話來(lái)說(shuō)就是可以讓你在類(lèi)或方法名上以打標(biāo)簽的形式讓該方法變得不一樣。具體怎么「不一樣」,這個(gè)在于你。
五、新建DataSourceChoose.java數(shù)據(jù)庫(kù)切換類(lèi)
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import cn.mayongfa.common.JdbcContextHolder;
public class DataSourceChoose {
//方法執(zhí)行前
public void before(JoinPoint point){
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
MethodSignature methodSignature = (MethodSignature)point.getSignature();
Class<?>[] parameterTypes = methodSignature.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m!=null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
JdbcContextHolder.clearJdbcType();
JdbcContextHolder.setJdbcType(data.value().getName());
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
這個(gè)其實(shí)是一個(gè)攔截器類(lèi),主要作用就是攔截那些方法名上有@DataSource這個(gè)自定義注解的,完了根據(jù)獲取注解的value()值,來(lái)做相應(yīng)的數(shù)據(jù)源切換。
到這里,整個(gè)讀寫(xiě)分離的分析及業(yè)務(wù)邏輯和具體代碼都完了,以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Spring AOP切面解決數(shù)據(jù)庫(kù)讀寫(xiě)分離實(shí)例詳解
- SpringMVC4+MyBatis+SQL Server2014實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫(xiě)分離
- SpringBoot集成Spring Data JPA及讀寫(xiě)分離
- Spring+MyBatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫(xiě)分離方案
- Spring配置動(dòng)態(tài)數(shù)據(jù)源實(shí)現(xiàn)讀寫(xiě)分離的方法
- Spring boot實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫(xiě)分離的方法
- 詳解使用spring aop實(shí)現(xiàn)業(yè)務(wù)層mysql 讀寫(xiě)分離
- Spring 實(shí)現(xiàn)數(shù)據(jù)庫(kù)讀寫(xiě)分離的示例
- 使用Spring AOP實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)讀寫(xiě)分離案例分析(附demo)
- Spring動(dòng)態(tài)數(shù)據(jù)源實(shí)現(xiàn)讀寫(xiě)分離詳解
相關(guān)文章
Java面試題沖刺第四天--數(shù)據(jù)庫(kù)
這篇文章主要為大家分享了最有價(jià)值的三道數(shù)據(jù)庫(kù)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07
關(guān)于springboot中對(duì)sqlSessionFactoryBean的自定義
這篇文章主要介紹了springboot中對(duì)sqlSessionFactoryBean的自定義方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
如何用注解的方式實(shí)現(xiàn)Mybatis插入數(shù)據(jù)時(shí)返回自增的主鍵Id
這篇文章主要介紹了如何用注解的方式實(shí)現(xiàn)Mybatis插入數(shù)據(jù)時(shí)返回自增的主鍵Id,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Java中如何將list轉(zhuǎn)為樹(shù)形結(jié)構(gòu)
這篇文章主要介紹了Java中如何將list轉(zhuǎn)為樹(shù)形結(jié)構(gòu),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
SpringBoot服務(wù)端數(shù)據(jù)校驗(yàn)過(guò)程詳解
這篇文章主要介紹了SpringBoot服務(wù)端數(shù)據(jù)校驗(yàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Maven多模塊之父子關(guān)系的創(chuàng)建
這篇文章主要介紹了Maven多模塊之父子關(guān)系的創(chuàng)建,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
IDEA 中使用 ECJ 編譯出現(xiàn) java.lang.IllegalArgumentException的錯(cuò)誤問(wèn)題
這篇文章主要介紹了IDEA 中使用 ECJ 編譯出現(xiàn) java.lang.IllegalArgumentException問(wèn)題 ,本文內(nèi)容簡(jiǎn)短給大家介紹的好,需要的朋友可以參考下2020-05-05

