aop+AbstractRoutingDataSource實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離片迅、負(fù)載均衡

1,動態(tài)數(shù)據(jù)源類

public class DynamicDataSource extends AbstractRoutingDataSource{

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDbType();//負(fù)載均衡,需要在這里進(jìn)行從庫的key輪詢驱闷。 
    }
}

2耻台,使用本地ThreadLocal,存儲當(dāng)前線程的數(shù)據(jù)源遗嗽。

threadlocal將具體的值保存在線程自身的threadLocalMap中

public abstract class DynamicDataSourceHolder {

    public static final String master = "master";

    public static final String slave = "slave";

    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();//定義全局ThreadLocal對象粘我,不同線程使用的是同一個對象。

    public static void setDbType(String dbTypeName){
        DynamicDataSourceHolder.threadLocal.set(dbTypeName);
    }

    public static String getDbType(){
        return DynamicDataSourceHolder.threadLocal.get();
    }

    public static void clearDbType(){
        DynamicDataSourceHolder.threadLocal.remove();
    }

}

3痹换,配置多個數(shù)據(jù)源

    @Value("${datasource.username2}")
    private String username2;

    @Value("${datasource.password2}")
    private String password2;

    @Value("${datasource.url2}")
    private String url2;

    @Value("${datasource.username}")
    private String username;

    @Value("${datasource.password}")
    private String password;

    @Value("${datasource.url}")
    private String url;

    @Bean//配置主數(shù)據(jù)庫源
    public DataSource master(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return getDataSource(druidDataSource);
    }
   
   @Bean//配置從數(shù)據(jù)庫源
    public DataSource slave(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(url2);
        druidDataSource.setUsername(username2);
        druidDataSource.setPassword(password2);
        return getDataSource(druidDataSource);
    }

    @Bean//配置動態(tài)數(shù)據(jù)源
    public DynamicDataSource dynamicDataSource(){
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(master());//default數(shù)據(jù)源
        Map<Object, Object> map = Maps.newHashMap();
        map.put(DynamicDataSourceHolder.master, master());//key - value
        map.put(DynamicDataSourceHolder.slave, slave());
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }

4征字,自定義注解,標(biāo)識使用哪個數(shù)據(jù)源

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface DynamicSourceAno {

    /**
     * 默認(rèn)使用的數(shù)據(jù)源
     * @return
     */

    String value() default DynamicDataSourceHolder.master;

}

5娇豫,實(shí)現(xiàn)切面匙姜。

設(shè)置spring事務(wù)aop的執(zhí)行順序@EnableTransactionManagement(order = 2) <tx:annotation-driven order="2"/>

@Aspect
@Component
@Order(1)//service層有@Transactional,事務(wù)也是aop實(shí)現(xiàn)的冯痢,這里定義order氮昧,要在事務(wù)開啟的aop執(zhí)行前,進(jìn)行數(shù)據(jù)源的切換浦楣,事務(wù)的aop結(jié)束后袖肥,進(jìn)行數(shù)據(jù)源的恢復(fù)默認(rèn)。
public class DynamicSourceAspect {

    private static Logger logger = LoggerFactory.getLogger(DynamicSourceAspect.class);

    @Before("within(@org.springframework.stereotype.Service *) && @annotation(dynamicSourceAno)")
//在service的所有方法執(zhí)行前振劳,并且?guī)в凶⒔釪ynamicSourceAno
    public void beforeDynamicSource(DynamicSourceAno dynamicSourceAno){

        try {
            DynamicDataSourceHolder.setDbType(dynamicSource.value());
        } catch (Throwable t) {
            logger.info(t.getMessage(), t);
        }

    }

    @After("within(@org.springframework.stereotype.Service *) && @annotation(dynamicSource)")
    public void afterDynamicSource(DynamicSource dynamicSource){

        try {
            DynamicDataSourceHolder.clearDbType();
        } catch (Throwable t) {
            logger.info(t.getMessage(), t);
        }

    }

}

6椎组,ThreadLocal與Thread

1)Thread類。定義了兩個TheadLocalMap類型屬性(可以用來保存對象)历恐,通過ThreadLocal類來維護(hù)的set寸癌,get,remove等操作弱贼。

image.png

2)ThreadLocal類蒸苇。ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類。
Entry類中value屬性用來保存和線程關(guān)聯(lián)的具體對象吮旅,key是ThreadLocal類型
image.png

3)ThreadLocal類操作當(dāng)前線程的ThreadLocalMap溪烤。

//保存value到當(dāng)前線程
public void set(T value) {
        Thread t = Thread.currentThread();//得到當(dāng)前線程
        ThreadLocalMap map = getMap(t);//得到當(dāng)前線程的ThreadLocalMap對象。
        if (map != null)
            map.set(this, value);//將自身對象作為key。每個線程保存這個對象氛什,都是使用相同的key莺葫,因?yàn)樵擃愋偷腡hreadLocal對象只new了一個。
        else
            createMap(t, value);
}

//當(dāng)?shù)谝淮握{(diào)用set方法時枪眉,會創(chuàng)建一個ThreadLocalMap對象。
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}
//從當(dāng)前線程的threadlocals中去除指定key對應(yīng)的value
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);//不同的線程使用相同的key來取值再层,得到的各自線程保存的對象贸铜。
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市聂受,隨后出現(xiàn)的幾起案子蒿秦,更是在濱河造成了極大的恐慌,老刑警劉巖蛋济,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍鳖,死亡現(xiàn)場離奇詭異,居然都是意外死亡碗旅,警方通過查閱死者的電腦和手機(jī)渡处,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祟辟,“玉大人医瘫,你說我怎么就攤上這事【衫В” “怎么了醇份?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吼具。 經(jīng)常有香客問我僚纷,道長,這世上最難降的妖魔是什么拗盒? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任怖竭,我火速辦了婚禮,結(jié)果婚禮上锣咒,老公的妹妹穿的比我還像新娘侵状。我一直安慰自己,他們只是感情好毅整,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布趣兄。 她就那樣靜靜地躺著,像睡著了一般悼嫉。 火紅的嫁衣襯著肌膚如雪艇潭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音蹋凝,去河邊找鬼鲁纠。 笑死,一個胖子當(dāng)著我的面吹牛鳍寂,可吹牛的內(nèi)容都是我干的改含。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼迄汛,長吁一口氣:“原來是場噩夢啊……” “哼捍壤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鞍爱,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鹃觉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后睹逃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盗扇,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年沉填,在試婚紗的時候發(fā)現(xiàn)自己被綠了疗隶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡拜轨,死狀恐怖抽减,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橄碾,我是刑警寧澤卵沉,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站法牲,受9級特大地震影響史汗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拒垃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一停撞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悼瓮,春花似錦戈毒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至命贴,卻和暖如春道宅,著一層夾襖步出監(jiān)牢的瞬間食听,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工污茵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留樱报,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓泞当,卻偏偏與公主長得像迹蛤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子襟士,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 7,638評論 4 30
  • 前言 ThreadLocal很多同學(xué)都搞不懂是什么東西笤受,可以用來干嘛。但面試時卻又經(jīng)常問到敌蜂,所以這次我和大家一起學(xué)...
    liangzzz閱讀 12,455評論 14 228
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,187評論 25 707
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司津肛,掛了不少章喉,但最終還是拿到小米、百度身坐、阿里秸脱、京東、新浪部蛇、CVTE摊唇、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,255評論 11 349
  • 我今年7月份畢業(yè),最近正好在工作單位實(shí)習(xí)涯鲁。很多大學(xué)生對畢業(yè)實(shí)習(xí)的態(tài)度就是四個字:應(yīng)付了事巷查。我覺得這是很可惜的一件事...
    葉泊菲閱讀 728評論 0 1