1. 程式人生 > >MyBatis學習筆記——外掛機制(AOP)

MyBatis學習筆記——外掛機制(AOP)

外掛

MyBatis所述的外掛功能,其實就是一個攔截器功能。

概述

1、在四大物件建立的時候,每個創建出來的物件不是直接返回的,而是通過interceptorChain.pluginAll(parameterHandler)返回的。
2、pluginAll獲取到所有的Interceptor(攔截器)(外掛需要實現的介面),呼叫Interceptor.pluginAll(target),返回target被包裝後的物件
3、外掛機制:我們可以使用外掛為目標物件建立一個代理物件:AOP(面向切面)
我們的外掛可以為四大物件創建出代理物件代理物件就可以攔截四大物件的每一個執行(方法)

 public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
 }

編寫單外掛

實現介面

實現自定義外掛,需要實現org.apache.ibatis.plugin.Interceptor介面。

/**
 * 完成外掛簽名:
 *      告訴MyBatis當前外掛用來攔截哪個物件的哪個方法
 */
@Intercepts
({ @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class) }) public class MyFirstPlugin implements Interceptor { /** * intercept:攔截 * 攔截目標物件的目標方法的執行: * */ @Override public Object intercept(Invocation invocation) throws
Throwable { System.out.println("MyFirstPlugin... intercept before:" + invocation.getMethod()); // 執行目標方法 Object proceed = invocation.proceed(); System.out.println("MyFirstPlugin... intercept after:" + invocation.getMethod()); // 返回執行後的返回值 return proceed; } /** * plugin: * 包裝目標物件:包裝,為目標物件建立一個代理物件 */ @Override public Object plugin(Object target) { System.out.println("MyFirstPlugin... plugin:mybatis將要包裝的物件" + target); // 我們可以藉助Plugin的wrap方法來使用擋圈Interceptor包裝我們目標物件 Object wrap = Plugin.wrap(target, this); // 返回為當前target建立的動態代理 return wrap; } /** * setProperties: * 將外掛註冊時的property屬性設定進來 * */ @Override public void setProperties(Properties properties) { System.out.println("外掛配置的資訊:" + properties); } }

註釋,接口裡面共有三個方法。
(1)intercept方法:
攔截目標方法,在其前後都可以進行業務邏輯,使用invocation.proceed();執行方法。
(2)plugin方法:
包裝target目標為代理物件,可以自己寫建立代理物件的邏輯,也可以使用MyBatis提供的快捷方式去建立代理物件Object wrap = Plugin.wrap(target, this);,第二個this就是當前的攔截器。
(3)setProperties方法:
在全域性配置檔案註冊外掛時,可以設定一些property屬性,通過該方法的入參獲取。
如以下的全域性配置檔案,設定了兩個property,可以通過入參獲取到:

    <!-- plugins:註冊外掛 -->
    <plugins>
        <plugin interceptor="com.shen.mybaties.dao.MyFirstPlugin">
            <property name="username" value="root" />
            <property name="password" value="123456" />
        </plugin>
    </plugins>

外掛簽名

光實現介面還不行,因為沒有指定攔截哪一個類,攔截哪一個方法
需要在外掛類上加一個@Intercepts,它有三個屬性
(1)type:類型別,就是some.class
(2)method:需要攔截的方法名
(3)args:由於可能存在方法過載,故需要通過引數型別來確定攔截的方法,同樣是類型別(.class

註冊外掛

全域性配置檔案加入如下配置:

<configuration>

    <!-- plugins:註冊外掛 -->
    <plugins>
        <plugin interceptor="com.shen.mybaties.dao.MyFirstPlugin">
            <property name="username" value="root" />
            <property name="password" value="123456" />
        </plugin>
    </plugins>

</configuration> 

測試

在plugin方法中,我們使用MyBatis提供的快捷方法Plugin.wrap(target, this);,可以快速得到一個代理物件。
這裡寫圖片描述
plugin產生的代理物件,其中target才是真正的物件。

編寫多外掛

模仿First外掛。

執行流程

多個外掛包裝物件時,每次都進行一次包裝,第二次傳入的target為第一次包裝的代理物件,造成一層包一層的情況。

for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
}

這裡寫圖片描述
建立動態代理的時候,是按照外掛配置順序建立層層代理的物件。
執行目標方法的時候,按照逆向順序執行。

簡單外掛,修改查詢引數

外掛開發,主要需要知道被代理物件有哪些屬性,如修改查詢引數。

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MetaObject metaObject = SystemMetaObject.forObject(invocation.getTarget());
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        metaObject.setValue("parameterHandler.parameterObject",2);
        // 執行目標方法
        Object proceed = invocation.proceed();
        // 返回執行後的返回值
        return proceed;
    }

這裡,需要知道被代理物件,有一個屬性為parameterHandler,屬性的屬性parameterObject是控制引數預編譯的引數,我們需要修改這個。
使用MyBatis提供的SystemMetaObjectgetValuesetValue方法進行修改,達到目的。