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提供的SystemMetaObject
的getValue
和setValue
方法進行修改,達到目的。