原理(JDK动态代理+责任链设计模式)
它有个代理类Plugin这个类同样会实现了InvocationHandler接口,当我们调用ParameterHandler,ResultSetHandler,StatementHandler,Executor的对象的时候,就会执行Plugin的invoke方法,Plugin在invoke方法中根据@Intercepts的配置信息(方法名,参数等)动态判断是否需要拦截该方法.再然后使用需要拦截的方法Method封装成Invocation,并调用Interceptor的proceed方法。这样我们就达到了拦截目标方法的结果。例如Executor的执行大概是这样的流程:
拦截器代理类对象->拦截器->目标方法
Executor.Method->Plugin.invoke->Interceptor.intercept->Invocation.proceed->method.invoke。
如何自定义拦截器?
- Interceptor接口
首先Mybatis官方早就想到我们开发会有这样的需求,所以开放了一个org.apacheibatis.plugin.Interceptor这样一个接口。
public interface Interceptor {
//当plugin函数返回代理,就可以对其中的方法进行拦截来调用intercept方法
Object intercept(Invocation invocation) throws Throwable;
//plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。
Object plugin(Object target);
//在Mybatis配置文件中指定一些属性
void setProperties(Properties properties);
}
- 自定义拦截器
@Intercepts({@Signature( type= Executor.class, method = "update", args ={MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
- 全局xml配置
最后如果你使用的是Mybatis.xml也就是Mybatis本身单独的配置,你可以需要在这里配置相应的拦截器名字等。如果你使用的是spring管理的Mybatis,那么你需要在Spring配置文件里面配置注册相应的拦截器。这样一个自定义mybatis插件流程大致就是这样了。
Mybatis四大核心接口
竟然Mybatis插件是对四大接口进行拦截的,那我们要先要知道Mybatis的四大接口对象 Executor,
StatementHandle, ResultSetHandler, ParameterHandler。
Mybatis Plugin 插件源码
经过上面的分析,再去看Mybastis Plugin 源码的时候就很轻松了。
这几个也就对应上面的几个,只不过添加了注解,来判断是否拦截指定方法。
- 拦截器链InterceptorChain
**
* 拦截器链
*
* @author Clinton Begin
*/
public class InterceptorChain {
/**
* 拦截器数组
*/
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 应用所有插件
*
* @param target 目标对象
* @return 应用结果
*/
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
通过初始化配置文件把所有的拦截器添加到拦截器链中。
public class Configuration {
protected final InterceptorChain interceptorChain = new InterceptorChain();
//创建参数处理器
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
//创建ParameterHandler
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//插件在这里插入
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
//创建结果集处理器
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
//创建DefaultResultSetHandler
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
//插件在这里插入
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
//创建语句处理器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建路由选择语句处理器
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//插件在这里插入
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
//产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
//这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
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);
}
//如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//此处调用插件,通过插件可以改变Executor行为
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
从代码可以看出Mybatis 在实例化Executor、ParameterHandler、ResultSetHandler、StatementHandler四大接口对象的时候调用interceptorChain.pluginAll()方法插入进去的。其实就是循环执行拦截器链所有的拦截器的plugin() 方法,mybatis官方推荐的plugin方法是Plugin.wrap() 方法,这个类就是我们上面的TargetProxy类。
- Plugin
/**
* 插件类,一方面提供创建动态代理对象的方法,另一方面实现对指定类的指定方法的拦截处理。
*
* @author Clinton Begin
*/
public class Plugin implements InvocationHandler {
/**
* 目标对象
*/
private final Object target;
/**
* 拦截器
*/
private final Interceptor interceptor;
/**
* 拦截的方法映射
*
* KEY:类
* VALUE:方法集合
*/
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;
}
/**
* 创建目标类的代理对象
*
* @param target 目标类
* @param interceptor 拦截器对象
* @return 代理对象
*/
public static Object wrap(Object target, Interceptor interceptor) {
// 获得拦截的方法映射
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 获得目标类的类型
Class<?> type = target.getClass();
// 获得目标类的接口集合
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
// 若有接口,则创建目标对象的 JDK Proxy 对象
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap)); // 因为 Plugin 实现了 InvocationHandler 接口,所以可以作为 JDK 动态代理的调用处理器
}
// 如果没有,则返回原始的目标对象
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<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
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<>();
// 循环递归 type 类,机器父类
while (type != null) {
// 遍历接口集合,若在 signatureMap 中,则添加到 interfaces 中
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
// 获得父类
type = type.getSuperclass();
}
// 创建接口的数组
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
}
- Interceptor
/**
* 拦截器接口
*
* @author Clinton Begin
*/
public interface Interceptor {
/**
* 拦截方法
*
* @param invocation 调用信息
* @return 调用结果
* @throws Throwable 若发生异常
*/
Object intercept(Invocation invocation) throws Throwable;
/**
* 应用插件。如应用成功,则会创建目标对象的代理对象
*
* @param target 目标对象
* @return 应用的结果对象,可以是代理对象,也可以是 target 对象,也可以是任意对象。具体的,看代码实现
*/
Object plugin(Object target);
/**
* 设置拦截器属性
*
* @param properties 属性
*/
void setProperties(Properties properties);
}
思路 这么下来思路就很清晰了,我们通过实现Interceptor类实现自定义拦截器,然后把它放入InterceptorChain(拦截器链)中,然后通过JDK动态代理来实现依次拦截处理。