#插件
- 四大對象在運行得時候,在Configuration對象得創建方法裏MyBatis使用了責任鏈得方式去封裝,也就是說我們可以有機會在四大對象調度得時候插入我們自定義得代碼區執行一些特殊得要求來滿足我們特殊得場景需求,就是MyBatis得插件技術.
插件的接口
MyBatis中使用插件,我們必須實現Interceptor接口,接口的代碼如下:
package org.apache.ibatis.plugin;
import java.util.Properties;
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
-
intercept():它將直接覆蓋你所攔截對象原有的方法,因此它是插件的核心方法。intercept裏面有個參數Invocation對象,通過它可以反射調度原來的對象的方法。
-
plugin():裏面的參數Object target是被攔截的對象,它的作用是給被攔截對象生成一個代理對象,並且返回它。在MyBatis裏直接使用org.apache.ibatis.plugin.Plugin中的wrap靜態(static)方法提供了生成代理對象,我們往往使用plugin方法便可以生成一個代理對象了。
public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; }
-
setProperties():允許在plugin元素中配置我們需要的參數,方法在插件初始化的時候就會被調用一次,然後把插件對象存入到配置中,方便後面獲取。 在org.apache.ibatis.builder.xml.XMLConfigBuilder我們可以看到: private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } } }
其實在解析配置文件的過程中,我們的插件節點和我們配置參數,同時使用反射技術生成對應的插件實例,然後調用插件方法中的setProperties(),設置我們配置的參數,然後將插件實例保存到配置對象裏,方便後面的讀取使用它。所以插件的實例對象是以開始就被初始化的,不是在使用到的時候才初始化,這樣可以提升性能。
Configuration保存插件的方式(初始化完的參數保存在了List<Interceptor> interceptors這個list裏面):
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
Plugin類的plugin()可以生成代理對象,在生成代理對象之後將對象存進了Configuration裏面,如果需要直接從Configuration實例獲取就可以了。從第一個對象(四大對象中的一個)開始,將對象傳遞給plugin(),然後返回一個代理;如果存在第二個插件,那麼我們拿到第一個代理對象,傳遞給plugin()再返回第一個代理獨享的代理......依此類推,攔截器越多,代理的對象就越多(多少個攔截器就多少個代理對象)。保證了每一個插件都可以攔截到真是的對象。這就號比每一個插件都可以一層層的處理攔截的對象。MyBatis的四大對象也是如此處理的。