如何利用SpringAOP 實現(xiàn)主從讀寫分離?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
成都創(chuàng)新互聯(lián)憑借專業(yè)的設(shè)計團(tuán)隊扎實的技術(shù)支持、優(yōu)質(zhì)高效的服務(wù)意識和豐厚的資源優(yōu)勢,提供專業(yè)的網(wǎng)站策劃、網(wǎng)站設(shè)計、成都網(wǎng)站制作、網(wǎng)站優(yōu)化、軟件開發(fā)、網(wǎng)站改版等服務(wù),在成都10年的網(wǎng)站建設(shè)設(shè)計經(jīng)驗,為成都近1000家中小型企業(yè)策劃設(shè)計了網(wǎng)站。
深刻討論為什么要讀寫分離?
為了服務(wù)器承載更多的用戶?提升了網(wǎng)站的響應(yīng)速度?分?jǐn)倲?shù)據(jù)庫服務(wù)器的壓力?就是為了雙機(jī)熱備又不想浪費(fèi)備份服務(wù)器?上面這些回答,我認(rèn)為都不是錯誤的,但也都不是完全正確的?!缸x寫分離」并不是多么神奇的東西,也帶不來多么大的性能提升,也許更多的作用的就是數(shù)據(jù)安全的備份吧。
從一個庫到讀寫分離,從理論上對服務(wù)器壓力來說是會帶來一倍的性能提升,但你仔細(xì)思考一下,你的應(yīng)用服務(wù)器真的很需要這一倍的提升么?那倒不如你去試著在服務(wù)器使用一下緩存系統(tǒng),如 Memcached、redis 這些分布式緩存,那性能可能是幾十倍的提升。而且,在服務(wù)器硬件異常強(qiáng)悍及性能廉價的今天,完全更沒必要了,所以,在今天,我認(rèn)為它更多的職責(zé)就是為了數(shù)據(jù)安全而設(shè)計的,同時又提升了一些性能,這樣也挺好。
可能我們更應(yīng)該稱之為主從分離
。
利用 AOP 實現(xiàn)讀寫分離
讀寫分離方式很簡單,就是在你讀數(shù)據(jù)是去連接從庫,在你寫數(shù)據(jù)的時候去連接主庫,具體代碼實現(xiàn)當(dāng)然就是連接時候去操作了,這沒什么難度,在代碼里寫就是了。可是,有追求的程序猿都是不是這么解決問題的呢!
其實通過上篇的 Spring AOP 攔截器的基本實現(xiàn) 我們知道 AOP 可以實現(xiàn)在方法開始執(zhí)行前后插入執(zhí)行我們想要的代碼,那這樣,我們是不是可以在執(zhí)行數(shù)據(jù)庫操作前根據(jù)業(yè)務(wù)來動態(tài)切換數(shù)據(jù)源呢?
思考一下這個方式理論上好像是可行的,這種方式首先不需要在業(yè)務(wù)代碼中去做切換,二是可能以后我們不需要讀寫分離了,把 AOP 切換的代碼去掉就行了,三是可能就是拓展性好了。
等不了了,開始擼代碼
你可能想深入的了解的話,我這里給你幾個程序里用到的關(guān)鍵字enum(枚舉)
、annotation(自定義注解)
、JoinPoint(注入點)
、AbstractRoutingDataSource(數(shù)據(jù)源接口子類)
,你理解了這些就知道了,其實你并不需要深入某些深層的東西,了解下即可。
一、建立JdbcContextHolder.java類
public class JdbcContextHolder { private static final ThreadLocalcontextHolder = new ThreadLocal (); 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(); } }
這個類的作用就是用來設(shè)置、獲取數(shù)據(jù)源連接
二、新建DynamicDataSource.java類,繼承于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(); } }
通過研究,我們知道determineCurrentLookupKey
方法是獲取相關(guān)數(shù)據(jù)源連接的,所以重寫determineCurrentLookupKey
方法就可以啦,然后我們?nèi)ネㄟ^剛剛我們建立的JdbcContextHolder
類去獲取。那怎么設(shè)置呢?
三、建立數(shù)據(jù)源DataSourceType.java枚舉類
public enum DataSourceType { //主庫 Master("master"), //從庫 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; } }
這個枚舉類的作用其實就是為了設(shè)置數(shù)據(jù)源而生的,它的目的就是讓設(shè)置數(shù)據(jù)源時更方便,如絲般順滑。
四、新建DataSource.java Annotation(自定義注解)類
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; }
自定義注解的意義不再過多討論,一句話來說就是可以讓你在類或方法名上以打標(biāo)簽的形式讓該方法變得不一樣。具體怎么「不一樣」,這個在于你。
五、新建DataSourceChoose.java數(shù)據(jù)庫切換類
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 } } }
這個其實是一個攔截器類,主要作用就是攔截那些方法名上有@DataSource
這個自定義注解的,完了根據(jù)獲取注解的value()
值,來做相應(yīng)的數(shù)據(jù)源切換。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。