PageHelper導(dǎo)致自定義Mybatis攔截器不生效

背景:

最近由于公司要做統(tǒng)一的數(shù)據(jù)變更記錄庇勃,以前是基于Aop來做的檬嘀,這樣效率很低,而且在做批量處理(insert,update,delete)操作時(shí)基本不可用责嚷。所以我打算使用CDC(如Canal,Maxwell等工具)來監(jiān)聽mysql的binlog來做鸳兽。但是不是所有的表都會(huì)有user_id字段,所以我們須要在sql上做一些處理罕拂,因?yàn)楣粳F(xiàn)在統(tǒng)一用的是mybatis揍异,那么現(xiàn)在我覺得比較好的方式就是在mybatis上進(jìn)行攔截改造sql.將userId從應(yīng)用層獲取到并寫入到須要執(zhí)行的sql上(只對(duì)insert,update,delete記錄)全陨。
如:有如下sql: update table set a= 1 where name =3
改造的結(jié)果就是:/** userId:1,traceId:123456**/ update table set a= 1 where name =3
這樣我們就可以記錄一次操作改了哪些數(shù)據(jù),改數(shù)據(jù)的人是哪個(gè)衷掷。

開始干:

這里面有幾個(gè)技術(shù)點(diǎn)辱姨,且都不怎么復(fù)雜,今天我們只聊mybatis攔截器戚嗅。其實(shí)寫一個(gè)攔截器還是很簡單的雨涛,網(wǎng)上有很多的代碼。代碼寫完后渡处,突然發(fā)現(xiàn)有些項(xiàng)目的自定義mybatis攔截器沒有生效镜悉。于是就開始google研究了一下,發(fā)現(xiàn)是因?yàn)槲覀冞@些不生效的項(xiàng)目使用了PageHelper.于是找了一些大神的解決方案医瘫,和攔截器的順序有關(guān)侣肄。先說一下結(jié)論:
MyBatis的攔截器采用責(zé)任鏈設(shè)計(jì)模式,多個(gè)攔截器之間的責(zé)任鏈?zhǔn)峭ㄟ^動(dòng)態(tài)代理組織的醇份。我們一般都會(huì)在攔截器中的intercept方法中往往會(huì)有invocation.proceed()語句稼锅,其作用是將攔截器責(zé)任鏈向后傳遞,本質(zhì)上便是動(dòng)態(tài)代理的invoke僚纷。

PageHelper在intercept方法中執(zhí)行完后沒有執(zhí)行invocation.proceed()矩距,意味著這玩意兒沒有繼續(xù)傳遞責(zé)任鏈(可能他有自己的想法)。所以他就沒有進(jìn)入我們自己的攔截器怖竭。

注意锥债,敲黑板:

A.不是所有的攔截器都必須要指定先后順序。
攔截器的調(diào)用順序分為兩大種痊臭,第一種是攔截的不同對(duì)象哮肚,例如攔截 Executor 和 攔截 StatementHandler 就屬于不同的攔截對(duì)象, 這兩類的攔截器在整體執(zhí)行的邏輯上是不同的广匙,在 Executor 中的 query 方法執(zhí)行過程中會(huì)調(diào)用StatementHandler允趟。

所以StatementHandler 屬于 Executor 執(zhí)行過程中的一個(gè)子過程。 所以這兩種不同類別的插件在配置時(shí)鸦致,一定是先執(zhí)行 Executor 的攔截器潮剪,然后才會(huì)輪到 StatementHandler。所以這種情況下配置攔截器的順序就不重要了分唾,在 MyBatis 邏輯上就已經(jīng)控制了先后順序抗碰。
所以如果你一個(gè)是Executor 類型的攔截器,一個(gè)是StatementHandler類型的攔截器绽乔,你可以不用管他順序改含,也就是說你只須要定義好類型都Executor的攔截器順序。

B.類型都為Executor的攔截器順序問題:
如果你的攔截器定義的順序是這樣的(你可以通過獲取sqlSessionFactory.getConfiguration()去查看里面的InterceptorChain然后看到各個(gè)interceptor的順序):
<plugins>
<plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor1"/>
<plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor2"/>
<plugin interceptor="com.github.pagehelper.ExecutorQueryInterceptor3"/>
</plugins>
他執(zhí)行的順序不是先執(zhí)行1,2,3,而執(zhí)行的順序是3,2,1捍壤。

Interceptor3:{
    Interceptor2: {
        Interceptor1: {
            target: Executor
        }
    }
}

從這個(gè)結(jié)構(gòu)應(yīng)該就很容易能看出來骤视,將來執(zhí)行的時(shí)候肯定是按照 3>2>1>Executor>1>2>3 的順序去執(zhí)行的。 可能有些人不知道為什么3>2>1>Executor之后會(huì)有1>2>3鹃觉,這是因?yàn)槭褂么頃r(shí)专酗,調(diào)用完代理方法后,還能繼續(xù)進(jìn)行其他處理盗扇。處理結(jié)束后祷肯,將代理方法的返回值繼續(xù)往外返回即可。

C(解決方案).因?yàn)镻ageHelper是Excetor類型的攔截器疗隶,所以按照前面兩條的理論佑笋,我們?nèi)绻胍赑ageHelper攔截器前面執(zhí)行,就必須要將我們自己的攔截器添加到他的攔截器后面斑鼻。

那該怎么做呢蒋纬?
我們可以通過這種方式來做:

我們?nèi)タ碢ageHelperAutoConfiguration的代碼是不是發(fā)現(xiàn),該類上面有一個(gè)@AutoConfigureAfter(MybatisAutoConfiguration.class)注解坚弱,這表明他是在MybatisAutoConfiguration加載完成后蜀备,才執(zhí)行自己的加載。那么我們是不是可以也可以構(gòu)建一個(gè)類似的代碼呢荒叶,雖然我們不是一個(gè)starter碾阁,但是我們可以通過這種操作來實(shí)現(xiàn)我們的須求。

(1)在src/main/resources/META-INF目錄下面些楣,創(chuàng)建一個(gè)spring.factories的文件
(2)spring.factories里的內(nèi)容是:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.llyt.exculd.TestLogAutoConfiguration
這個(gè)com.llyt.exculd.TestLogAutoConfiguration脂凶,就是你自己的配置類的全路徑。該類的代碼在后面愁茁。
(3) TestLogAutoConfiguration代碼:

@Configuration
@AutoConfigureAfter(PageHelperAutoConfiguration.class)
public class TestLogAutoConfiguration {
    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @PostConstruct
    public void addMyInterceptor() {
        ExampleOnePlugin e = new ExampleOnePlugin();
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            sqlSessionFactory.getConfiguration().addInterceptor(e);
        }
    }
}

至此蚕钦,這種方法就OK了。但是你可能會(huì)執(zhí)行不成功埋市,該類的addMyInterceptor方法總是先于PageHelperAutoConfiguration的addPageInterceptor()方法執(zhí)行冠桃,這就意味著你的攔截器總是添加到在pageHelper攔截前面的命贴,那么他總是在PageHelper攔截器后面執(zhí)行道宅。
如果出現(xiàn)這種情況,說明你可能在spring boot主類上配置了
@ComponentScan("****")胸蛛,且該類會(huì)被這個(gè)掃描到污茵,這個(gè)就是導(dǎo)致的原因所在。

這里面有一個(gè)知識(shí)點(diǎn)就是葬项,不是配置了@AutoConfigureAfter(A.class)就一定表示該類一定在A類后面執(zhí)行泞当。

如果配置類在 spring.factories 中配置了且而如果你的類被自己 Spring Boot 啟動(dòng)類掃描到了,那么該類會(huì)被會(huì)優(yōu)先掃描到民珍,配置類對(duì)順序有要求時(shí)就會(huì)出錯(cuò)襟士。

那么該怎么解決呢盗飒?

解決的方法有兩個(gè):

a.使用騷操作。

如果你將自己的配置類放到特別的包下陋桂,不使用 Spring Boot 啟動(dòng)類掃描逆趣。完全通過 spring.factories 讀取配置就可以實(shí)現(xiàn)這個(gè)目的。
比如嗜历,你@ComponentScan掃描的包是com.bb.cc,那么你就將該配置類放在com.bb.dd包下面宣渗。

b.如果你覺得上面這種不習(xí)慣,可以用使用excludeFilters :
@ComponentScan(basePackages = {"com.llyt"},  excludeFilters = @ComponentScan.Filter(
     type = FilterType.REGEX,
     pattern = "com.llyt.exculd.*"))

將你的配置類放在com.llyt.exculd包下面就行了梨州。

至此痕囱,mybatis攔截器的不生效的問題,搞完了暴匠。

參考文獻(xiàn):
http://xtong.tech/2018/08/01/MyBatis%E6%8B%A6%E6%88%AA%E5%99%A8%E5%9B%A0pagehelper%E8%80%8C%E5%A4%B1%E6%95%88%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/Interceptor.md

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巷查,隨后出現(xiàn)的幾起案子岛请,更是在濱河造成了極大的恐慌,老刑警劉巖盅称,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缩膝,死亡現(xiàn)場離奇詭異岸霹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)痛黎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門湖饱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來井厌,“玉大人,你說我怎么就攤上這事器赞。” “怎么了拳魁?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵撮弧,是天一觀的道長。 經(jīng)常有香客問我授舟,道長贸辈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任奢啥,我火速辦了婚禮嘴拢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赌结。我一直安慰自己柬姚,他們只是感情好庄涡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布穴店。 她就那樣靜靜地躺著迹鹅,像睡著了一般贞言。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弟蚀,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天义钉,我揣著相機(jī)與錄音昧绣,去河邊找鬼。 笑死捶闸,一個(gè)胖子當(dāng)著我的面吹牛夜畴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播删壮,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贪绘,長吁一口氣:“原來是場噩夢啊……” “哼央碟!你這毒婦竟也來了税灌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤亿虽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后洛勉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粘秆,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年收毫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牛哺。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡引润,死狀恐怖巩趁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情别凹,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布炉菲,位于F島的核電站拍霜,受9級(jí)特大地震影響嘱丢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祠饺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一越驻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧道偷,春花似錦缀旁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祝旷。三九已至履澳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怀跛,已是汗流浹背距贷。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吻谋,地道東北人阁最。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓低千,卻偏偏與公主長得像,于是被迫代替她去往敵國和親难审。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘫拣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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

  • 1.It's time for breakfast. 到吃早餐的時(shí)間了地淀。 2.Comeand eat your b...
    秀火鍋閱讀 90評(píng)論 0 0
  • 今年下半年失球,接到了新一個(gè)項(xiàng)目——?jiǎng)趧?dòng)關(guān)系協(xié)調(diào)員考證班,但前期一直在忙單位的事帮毁,還有其他項(xiàng)目实苞,沒有更多時(shí)間投入,以為...
    無憂俠閱讀 242評(píng)論 0 1
  • 霧柳如煙烈疚,清荷倩影映湖天黔牵。每為看花心意篤,幽波淥爷肝,并蒂真情蓮不俗猾浦。 ?
    郭大牛閱讀 567評(píng)論 6 16
  • 身為一名鄉(xiāng)村教師,以前我總想著如何在有限的條件下帶給孩子們更多專業(yè)而多姿的課程灯抛,讓孩子們的見識(shí)走出大山金赦,領(lǐng)略世界的...
    梧州市三貴小學(xué)李玉姬閱讀 355評(píng)論 0 0