<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
攔截器的作用就是我們可以攔截某些方法的呼叫,在目標方法前後加上我們自己邏輯
Mybatis攔截器設計的一個初衷是為了供使用者在某些時候可以實現自己的邏輯而不必去動Mybatis固有的邏輯。
mybatis 自定義攔截器
1、實現Interceptor 介面,並新增攔截註解 @Intercepts
2、組態檔中新增攔截器
mybatis 攔截器預設可攔截的型別只有四種
Executor
:攔截執行器的方法。ParameterHandler
:攔截引數的處理。ResultHandler
:攔截結果集的處理。StatementHandler
:攔截Sql語法構建的處理。對於我們的自定義攔截器必須使用 mybatis 提供的註解來指明我們要攔截的是四類中的哪一個類介面
具體規則如下:
a:Intercepts 攔截器:標識我的類是一個攔截器
b:Signature 署名:則是指明我們的攔截器需要攔截哪一個介面的哪一個方法
type
對應四類介面中的某一個,比如是 Executormethod
對應介面中的哪類方法,比如 Executor 的 update 方法args
對應介面中的哪一個方法,比如 Executor 中 query 因為過載原因,方法有多個,args 就是指明引數型別,從而確定是哪一個方法@Intercepts({ @Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}), @Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class}) }) public class MyInterceptor implements Interceptor { /** * 這個方法很好理解 * 作用只有一個:我們不是攔截方法嗎,攔截之後我們要做什麼事情呢? * 這個方法裡面就是我們要做的事情 * * 解釋這個方法前,我們一定要理解方法引數 {@link Invocation} 是個什麼鬼? * 1 我們知道,mybatis攔截器預設只能攔截四種型別 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler * 2 不管是哪種代理,代理的目標物件就是我們要攔截物件,舉例說明: * 比如我們要攔截 {@link Executor#update(MappedStatement ms, Object parameter)} 方法, * 那麼 Invocation 就是這個物件,Invocation 裡面有三個引數 target method args * target 就是 Executor * method 就是 update * args 就是 MappedStatement ms, Object parameter * * 如果還是不能理解,我再舉一個需求案例:看下面方法程式碼裡面的需求 * * 該方法在執行時呼叫 */ @Override public Object intercept(Invocation invocation) throws Throwable { /* * 需求:我們需要對所有更新操作前列印查詢語句的 sql 紀錄檔 * 那我就可以讓我們的自定義攔截器 MyInterceptor 攔截 Executor 的 update 方法,在 update 執行前列印sql紀錄檔 * 比如我們攔截點是 Executor 的 update 方法 : int update(MappedStatement ms, Object parameter) * * 那當我們紀錄檔列印成功之後,我們是不是還需要呼叫這個query方法呢,如何如呼叫呢? * 所以就出現了 Invocation 物件,它這個時候其實就是一個 Executor,而且 method 對應的就是 query 方法,我們 * 想要呼叫這個方法,只需要執行 invocation.proceed() */ /* 因為我攔截的就是Executor,所以我可以強轉為 Executor,預設情況下,這個Executor 是個 SimpleExecutor */ Executor executor = (Executor)invocation.getTarget(); /* * Executor 的 update 方法裡面有一個引數 MappedStatement,它是包含了 sql 語句的,所以我獲取這個物件 * 以下是虛擬碼,思路: * 1 通過反射從 Executor 物件中獲取 MappedStatement 物件 * 2 從 MappedStatement 物件中獲取 SqlSource 物件 * 3 然後從 SqlSource 物件中獲取獲取 BoundSql 物件 * 4 最後通過 BoundSql#getSql 方法獲取 sql */ MappedStatement mappedStatement = ReflectUtil.getMethodField(executor, MappedStatement.class); SqlSource sqlSource = ReflectUtil.getField(mappedStatement, SqlSource.class); BoundSql boundSql = sqlSource.getBoundSql(args); String sql = boundSql.getSql(); logger.info(sql); /* * 現在紀錄檔已經列印,需要呼叫目標物件的方法完成 update 操作 * 我們直接呼叫 invocation.proceed() 方法 * 進入原始碼其實就是一個常見的反射呼叫 method.invoke(target, args) * target 對應 Executor物件 * method 對應 Executor的update方法 * args 對應 Executor的update方法的引數 */ return invocation.proceed(); } /** * 這個方法也很好理解 * 作用就只有一個:那就是Mybatis在建立攔截器代理時候會判斷一次,當前這個類 MyInterceptor 到底需不需要生成一個代理進行攔截, * 如果需要攔截,就生成一個代理物件,這個代理就是一個 {@link Plugin},它實現了jdk的動態代理介面 {@link InvocationHandler}, * 如果不需要代理,則直接返回目標物件本身 * * Mybatis為什麼會判斷一次是否需要代理呢? * 預設情況下,Mybatis只能攔截四種型別的介面:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler * 通過 {@link Intercepts} 和 {@link Signature} 兩個註解共同完成 * 試想一下,如果我們開發人員在自定義攔截器上沒有指明型別,或者隨便寫一個攔截點,比如Object,那Mybatis瘋了,難道所有物件都去攔截 * 所以Mybatis會做一次判斷,攔截點看看是不是這四個介面裡面的方法,不是則不攔截,直接返回目標物件,如果是則需要生成一個代理 * * 該方法在 mybatis 載入核心組態檔時被呼叫 */ @Override public Object plugin(Object target) { /* * 看了這個方法註釋,就應該理解,這裡的邏輯只有一個,就是讓mybatis判斷,要不要進行攔截,然後做出決定是否生成一個代理 * * 下面程式碼什麼鬼,就這一句就搞定了? * Mybatis判斷依據是利用反射,獲取這個攔截器 MyInterceptor 的註解 Intercepts和Signature,然後解析裡面的值, * 1 先是判斷要攔截的物件是四個型別中 Executor、StatementHandler、ParameterHandler、 ResultSetHandler 的哪一個 * 2 然後根據方法名稱和引數(因為有過載)判斷對哪一個方法進行攔截 Note:mybatis可以攔截這四個介面裡面的任一一個方法 * 3 做出決定,是返回一個物件呢還是返回目標物件本身(目標物件本身就是四個介面的實現類,我們攔截的就是這四個型別) * * 好了,理解邏輯我們寫程式碼吧~~~ What !!! 要使用反射,然後解析註解,然後根據引數型別,最後還要生成一個代理物件 * 我一個小白我怎麼會這麼高大上的程式碼嘛,怎麼辦? * * 那就是使用下面這句程式碼吧 哈哈 * mybatis 早就考慮了這裡的複雜度,所以提供這個靜態方法來實現上面的邏輯 */ return Plugin.wrap(target, this); } /** * 這個方法最好理解,如果我們攔截器需要用到一些變數引數,而且這個引數是支援可設定的, * 類似Spring中的@Value("${}")從application.properties檔案獲取 * 這個時候我們就可以使用這個方法 * * 如何使用? * 只需要在 mybatis 組態檔中加入類似如下設定,然後 {@link Interceptor#setProperties(Properties)} 就可以獲取引數 * <plugin interceptor="liu.york.mybatis.study.plugin.MyInterceptor"> * <property name="username" value="LiuYork"/> * <property name="password" value="123456"/> * </plugin> * 方法中獲取引數:properties.getProperty("username"); * * 問題:為什麼要存在這個方法呢,比如直接使用 @Value("${}") 獲取不就得了? * 原因是 mybatis 框架本身就是一個可以獨立使用的框架,沒有像 Spring 這種做了很多依賴注入的功能 * * 該方法在 mybatis 載入核心組態檔時被呼叫 */ @Override public void setProperties(Properties properties) { String username = properties.getProperty("username"); String password = properties.getProperty("password"); // TODO: 2019/2/28 業務邏輯處理... } }
三個核心方法都加了詳細的註釋,而且結合案例需求說明問題
那麼多文字不想行看,沒關係有概括
總結:
Executor
:攔截執行器的方法。ParameterHandler
:攔截引數的處理。ResultHandler
:攔截結果集的處理。StatementHandler
:攔截Sql語法構建的處理。@Intercepts
:標識該類是一個攔截器;@Signature
:指明自定義攔截器需要攔截哪一個型別,哪一個方法;2.1 type:對應四種型別中的一種;
2.2 method:對應介面中的哪個方法;
2.3 args:對應哪一個方法引數型別(因為可能存在過載方法);
接下來我們看看 Plugin 類
package org.apache.ibatis.plugin; /** * Plugin 類其實就是一個代理類,因為它實現了jdk動態代理介面 InvocationHandler * 我們核心只需要關注兩個方法 * wrap: * 如果看懂了程式碼案例1的例子,那麼這個方法很理解,這個方法就是 mybatis 提供給開發人員使用的一個工具類方法, * 目的就是幫助開發人員省略掉 反射解析註解 Intercepts 和 Signature,有興趣的可以去看看原始碼 Plugin#getSignatureMap 方法 * * invoke: * 這個方法就是根據 wrap 方法的解析結果,判斷當前攔截器是否需要進行攔截, * 如果需要攔截:將 目標物件+目標方法+目標引數 封裝成一個 Invocation 物件,給我們自定義的攔截器 MyInterceptor 的 intercept 方法 * 這個時候就剛好對應上了上面案例1中對 intercept 方法的解釋了,它就是我們要處理自己邏輯的方法, * 處理好了之後是否需要呼叫目標物件的方法,比如上面說的 列印了sql語句,是否還要查詢資料庫呢?答案是肯定的 * 如果不需要攔截:則直接呼叫目標物件的方法 * 比如直接呼叫 Executor 的 update 方法進行更新資料庫 * */ class Plugin implements InvocationHandler { public static Object wrap(Object target, Interceptor interceptor) { // 省略 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 省略 } }
貼一段網上的通用解釋吧:
這就是Mybatis中實現Interceptor攔截的一個思想
在springboot中要給mybatis加上這個攔截器,有三種方法,前兩種方法在啟動專案時不會自動呼叫自定義攔截器的setProperties方法。
攔截器順序
1、不同攔截器順序
Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
2、對於同一個型別的攔截器的不同物件攔截順序:
在 mybatis 核心組態檔根據設定的位置,攔截順序是 從上往下
直接給自定義攔截器新增一個 @Component註解,當呼叫sql時結果如下,可以看到攔截器生效了,但是啟動時候並沒有自動呼叫setProperties方法。
@Component @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) public class MybatisInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //業務程式碼 } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub } }
在設定類裡新增攔截器,這種方法結果同上,也不會自動呼叫setProperties方法。
@Configuration public class MybatisConfig { @Bean public ConfigurationCustomizer mybatisConfigurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { configuration.addInterceptor(new MybatisInterceptor()); } }; } }
這種方法就是跟以前的設定方法類似,在yml組態檔中指定mybatis的xml組態檔,
注意:config-location屬性和configuration屬性不能同時指定
mybatis: config-location: classpath:mybatis.xml type-aliases-package: me.zingon.pagehelper.model mapper-locations: classpath:mapper/*.xml
mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="me.zingon.pacargle.model"/> </typeAliases> <plugins> <plugin interceptor="me.zingon.pagehelper.interceptor.MyPageInterceptor"> <property name="dialect" value="oracle"/> </plugin> </plugins> </configuration>
可以看到,在啟動專案的時候setProperties被自動呼叫了
總結:前兩種方法可以在初始化自定義攔截器的時候通過 @Value 註解直接初始化需要的引數。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45