為什么要有插件
可以在映射語句執(zhí)行前后加一些自定義的操作,比如緩存失尖、分頁等 <br />
可以攔截哪些方法
默認情況下善榛,Mybatis允許使用插件來攔截的方法有:
- Executor:update戴而、query砾肺、flushStatements挽霉、commit、rollback变汪、getTransaction侠坎、close、isClosed疫衩。
實現(xiàn)類:SimpleExecutor/BatchExecutor/ReuseExecutor/CachingExecutor - ParameterHandler:getParameterObject硅蹦、setParameters。
實現(xiàn)類:DefaultParameterHandler - ResultSetHandler:handleResultSets闷煤、handleOutputParameters童芹。
實現(xiàn)類:DefaultResultSetHandler - StatementHandler:prepare、parameterize鲤拿、batch假褪、update、query近顷。
實現(xiàn)類:CallableStatementHandler/PreparedStatementHandler/SimpleStatementHandler/RoutingStatementHandler
如何自定義插件
只需實現(xiàn)Interceptor接口生音,并指定要攔截的方法簽名
@Intercepts({
@Signature(
type=Executor.class,method="update",args={ MappedStatement.class,Object.class })
})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
//自定義實現(xiàn)
return invocation.proceed();
}
public Object plugin(Object target){
return Plugin.wrap(target,this)
}
public void setProperties(Properties properties){
//傳入配置項
String size = properties.getProperty("size");
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<!-- 這里的配置項就傳入setProperties方法中 -->
<property name="size" value="100">
</plugin>
</plugins>
攔截器實現(xiàn)原理
如果了解Mybatis的攔截器實現(xiàn)原理,可以在以后的工作中也可使用該方法實現(xiàn)自己的攔截器
//攔截器接口窒升,供外部實現(xiàn)缀遍,實現(xiàn)該接口就定義了一個插件
public interface Interceptor {
//攔截方法,可以將自定義邏輯寫在該方法中
Object intercept(Invocation invocation) throws Throwable;
//包裝成插件饱须,一般Plugin.wrap(target,this)就行了
Object plugin(Object target);
//傳入自定義配置參數(shù)
void setProperties(Properties properties);
}
攔截器上定義的注解
@Intercepts:攔截器注解域醇,包括一個或多個@Signature,攔截的目標類信息
@Signature:攔截的目標類信息蓉媳,包括type譬挚、method、args酪呻,一個@Intercepts中可包含多個@Signature
public class Invocation {
private Object target;//目標對象
private Method method;//調(diào)用方法
private Object[] args;//方法形參列表
//省略get和set方法
//執(zhí)行調(diào)用减宣,基于動態(tài)代理,在Interceptor的intercept方法中一定要調(diào)用該方法
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
//動態(tài)代理實現(xiàn)
public class Plugin implements InvocationHandler {
private Object target;
private Interceptor interceptor;//攔截器
private Map<Class<?>, Set<Method>> signatureMap;//攔截目標類的目標方法
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
//包裝目標實例
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
//目標類所有接口是否有signatureMap中定義的Class
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
//如果攔截器中有定義攔截目標類中的方法時玩荠,就返回代理實例
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
//沒有就返回目標實例
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//該方法需要攔截
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
//獲取攔截器上的SignatureMap
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.get(sig.type());//重復(fù)定義的只生效一個
if (methods == null) {
methods = new HashSet<Method>();
signatureMap.put(sig.type(), methods);
}
try {
//獲取目標類中的指定方法
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<Class<?>>();
while (type != null) {//獲取type上的所有接口
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {//這里不判斷Method,只判斷Class<?>
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
}
在配置文件中定義的過濾器漆腌,都保存在Configuration類的interceptorChain中,這個類保存了mybatis的所有配置阶冈,interceptorChain類中保存中所有Interceptor集合組成的攔截器鏈屉凯,這個鏈是如何添加進去的呢?請看源碼眼溶。
//XMLConfigBuilder類中解析mybatis-config.xml 核心方法parseConfiguration(XNode root)
pluginElement(root.evalNode("plugins"));//插件配置項
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//遍歷 plugins的子節(jié)點plugin
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");//獲取interceptor屬性值
Properties properties = child.getChildrenAsProperties();//獲取plugin屬性值
//創(chuàng)建攔截器實例悠砚,這里interceptor值也可以是typeAlias注冊的簡名
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//設(shè)置屬性項
interceptorInstance.setProperties(properties);
//添加到interceptorChain中
configuration.addInterceptor(interceptorInstance);
}
}
}
//Configuration類,添加攔截器
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
攔截的哪些接口
//SQL語句處理器
public interface StatementHandler {
//預(yù)備工作
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
//參數(shù)處理
void parameterize(Statement statement) throws SQLException;
//批量處理
void batch(Statement statement) throws SQLException;
//更新處理
int update(Statement statement) throws SQLException;
//查詢處理
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
}
//返回集處理器
public interface ResultSetHandler {
//處理返回結(jié)果
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
//處理輸出參數(shù)
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
//參數(shù)處理器
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps) throws SQLException;
}
如何攔截這些接口
//創(chuàng)建相應(yīng)Handler時會將所有攔截器通過動態(tài)代理方式返回代理Handler
public class Configuration {
//創(chuàng)建ParameterHandler(參數(shù)處理器)
public ParameterHandler newParameterHandler(MappedStatement mappedStatement,
Object parameterObject, BoundSql boundSql) {
// 根據(jù)指定Lang(默認RawLanguageDriver),創(chuàng)建ParameterHandler堂飞,將實際參數(shù)傳遞給JDBC語句
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(
mappedStatement, parameterObject, boundSql);
//返回代理實例
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
//創(chuàng)建ResultSetHandler(結(jié)果處理器)
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement,
RowBounds rowBounds,ParameterHandler parameterHandler,
ResultHandler resultHandler,BoundSql boundSql) {
//默認使用DefaultResultSetHandler創(chuàng)建ResultSetHandler實例
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement,
parameterHandler, resultHandler, boundSql, rowBounds);
//返回代理實例
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
//創(chuàng)建StatementHandler(SQL語句處理器)
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//默認使用RoutingStatementHandler(路由作用)
//創(chuàng)建指定StatementHandler實例(默認SimpleStatementHandler)
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement,
parameterObject, rowBounds, resultHandler, boundSql);
//返回代理實例
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
//創(chuàng)建Executor(執(zhí)行器)
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//獲取executorType灌旧,默認是SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
//這一行感覺有點多余啊
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) { //批量執(zhí)行
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) { //重用
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);//通用
}
if (cacheEnabled) { //開啟緩存
executor = new CachingExecutor(executor);
}
//返回代理實例
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
//執(zhí)行器
public interface Executor {
//更新
int update(MappedStatement ms, Object parameter) throws SQLException;
//查詢(先查緩存)
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
//查詢
<E> List<E> query(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
//查詢游標
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds)
throws SQLException;
//刷新Statement
List<BatchResult> flushStatements() throws SQLException;
//提交事務(wù)
void commit(boolean required) throws SQLException;
//回滾事務(wù)
void rollback(boolean required) throws SQLException;
//創(chuàng)建緩存key
CacheKey createCacheKey(MappedStatement ms, Object parameterObject,RowBounds rowBounds,
BoundSql boundSql);
//是否存在key
boolean isCached(MappedStatement ms, CacheKey key);
//清除本地緩存
void clearLocalCache();
//延遲加載
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key,
Class<?> targetType);
//獲取事務(wù)
Transaction getTransaction();
//關(guān)閉連接
void close(boolean forceRollback);
//是否關(guān)閉
boolean isClosed();
//設(shè)置Executor
void setExecutorWrapper(Executor executor);
}
總結(jié)
當然具體實現(xiàn)肯定不止這么多代碼,如果需要了解绰筛,需要自行看源碼枢泰,下面坐下總結(jié)。
1.攔截器實現(xiàn)
Interceptor接口供插件實現(xiàn)铝噩,@Intercepts注解在插件實現(xiàn)上衡蚂,表示這是一個插件類并配置將要攔截哪些方法,@Signature定義將要攔截的方法信息,如名稱/類型/形參列表,Plugin類實現(xiàn)了InvocationHandler接口毛甲,是動態(tài)代理的具體實現(xiàn)年叮,Invocation類包裝了攔截的目標實例,InterceptorChain保存所有攔截器玻募。
2.如何實現(xiàn)攔截
創(chuàng)建目標實例只损,比如A a = new A();
Interceptor interceptor = new LogInterceptor();//如果攔截a中的save方法
將A b = (A)interceptor.plugin(a);這里b就是a的代理實例,在調(diào)用a中的save方法時七咧,實際將調(diào)用interceptor的intercept方法跃惫,在該方法中一定要調(diào)用Invocation的proceed方法并將返回值返回。