mybatis源码下载
https://github.com/mybatis/mybatis-3,本文分析源码版本3.4.5
mybatis启动大致流程
在看这篇文章前,建议查看我另一篇文章,以了解框架启动的流程和框架中一些重要对象:https://blog.csdn.net/Aqu415/article/details/79049739
可以被插件代理的类
Mybatis中可以被插件的类分为4种,分别是Executor、StatementHandler、ParameterHandler、ResultSetHandler。
InterceptorChain
这里说一个在插件开发过程中比较重要的类,这个类会缓存系统中所有的插件类(一个Configuration对应一个InterceptorChain);InterceptorChain中缓存的插件会在框架启动时伴随解析配置文件中plugins节点完成
InterceptorChain代码如下:
package org.apache.ibatis.plugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Clinton Begin
*/
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);
}
}
我们看看哪些地方调用了该方法:
调用这个方法的地方有如下(可见上面说的四大对象都是由Configuration这个类创建的):
1、org.apache.ibatis.session.Configuration#newExecutor
2、org.apache.ibatis.session.Configuration#newStatementHandler
3、org.apache.ibatis.session.Configuration#newParameterHandler
4、org.apache.ibatis.session.Configuration#newResultSetHandler
目标对象被代理的时机
我们挑第一个方法来看看,发现目标对象被代理的时机在对象创建后直接调用interceptorChain.pluginAll方法完成的。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
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);
}
// @A
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
@A:调用InterceptorChain对已经创建好的Executor对象进行代理(插件化包装),调用pluginAll方法实则是调用每一个插件的plugin方法
如下是 Executor 的方法定义:
package org.apache.ibatis.executor;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* @author Clinton Begin
*/
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
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;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
这个类主要数据库操作相关的顶级接口,如果要在执行数据库操作较早时机对方法进行拦截,可以对Executor进行插件编写,接口里定义的方法都可以被插件拦截到。
插件的编写
需要实现 org.apache.ibatis.plugin.Interceptor接口,编程模式可以参考框架提供的例子类
package org.apache.ibatis.builder;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import java.util.Properties;
// @B
@Intercepts({})
public class ExamplePlugin implements Interceptor {
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// @A
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
public Properties getProperties() {
return properties;
}
}
@A:调用Plugin类生成一个代理类,返回这个代理对象
@B:完善需要拦截的目标类,指定方法和参数类型(作用后面分析)
工具类Plugin
框架提供一个通用生成代理的工具类 org.apache.ibatis.plugin.Plugin,其实我们可以用Plugin的api来包装目标对象也可以采用自定义的方式;采用Plugin是为了后面更方便使用@Intercepts等注解对目标类和目标方法进行过滤,而不是对所有以上四种类型的所有方法进行拦截;
因为在Plugin内部方法拦截时做了特殊过滤,使用起来更方便。
我们来看看Plugin 类
package org.apache.ibatis.plugin;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.reflection.ExceptionUtil;
/**
* @author Clinton Begin
*/
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final 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();
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);
}
}
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
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());
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) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
}
这个类其实兼有两大作用
1、生成代理,代理即目标对象与自定义逻辑间的枢纽拦截器(对应wrap方法)
2、自己充当这个拦截器(基于jdk代理模式)
如果分开成两个类的话,分析起来会更清晰一些
invoke
我们知道基于JDK动态代理的代理对象,会在目标方法前执行其invoke方法;我们分析一下这个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// @A
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);
}
}
@A:这里从缓存里看是否有配置目标类和方法,如果有的话则执行拦截器的intercept方法,也就是插件的intercept方法;
目标类和目标方法是在创建代理对象的时候,通过getSignatureMap返回;其主要是解析插件上的Intercepts注解
插件对真实对象方法拦截时机
1、从以上分析如果有多个插件,那么插件之间会形成上一个插件代理下一个插件的情况;
2、由于生成四大对象时,对目标对象进行了org.apache.ibatis.plugin.InterceptorChain#pluginAll包装,那么框架真正得到的对象就是代理对象,那么其执行方法的时候就是拦截时机
Object intercept(Invocation invocation) throws Throwable;
我们接下来分析一下执行插件的intercept方法时候,传给它的参数具体是什么;其类型是:org.apache.ibatis.plugin.Invocation
package org.apache.ibatis.plugin;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Clinton Begin
*/
public class Invocation {
// @A
private final Object target;
// @B
private final Method method;
// @C
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
@A:目标对象,即Executor、StatementHandler、ParameterHandler、ResultSetHandler四大类型的真实对象;如果是多层插件代理同一个类型又可能是原始对象,但也有可能是上一个代理对象
@B:方法签名
@C:参数