首先自定义拦截器要实现Interceptor接口
先来个简单的一个例子
/**
* @Intercepts 表明这个是个拦截器
* @Signature 表明要拦截的类,类里面的方法,方法里的参数
* 这里要拦截的是Executor,Executor里面的update方法,方法的参数
*/
@Intercepts(@Signature(type = Executor.class, method = "update" ,args = {MappedStatement.class, Object.class}))
public class SqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 业务逻辑....
// 推进责任链前进,调用下一个拦截器拦截的方法
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
Interceptor 接口
public interface Interceptor {
// 拦截后的业务逻辑实现
Object intercept(Invocation var1) throws Throwable;
// 将当前拦截器到注册到拦截器链中
Object plugin(Object var1);
// 设置自定义参数
void setProperties(Properties var1);
}
plugin方法在哪里被调用呢,往下看
下面的代码来自在org.apache.ibatis.session.Configuration类
作用是创建mybatis的四大对象,可以看出方法里都调用了interceptorChain类中的pluginAll方法
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
ResultSetHandler resultSetHandler = (ResultSetHandler)this.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 = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return this.newExecutor(transaction, this.defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
interceptorChain里面保存着所有拦截器
在pluginAll方法里调用了plugin方法在这里被调用了
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList();
// ....
public Object pluginAll(Object target) {
Interceptor interceptor;
// 每个拦截器都对四大对象进行代理,也就说明了拦截器只支持拦截这几个对象
for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)var2.next();
}
return target;
}
}
上面讲完了Mybatis在哪调用了plugin方法,现在讲下怎么实现plugin方法。
Mybatis给我们提供了一个Plugin类用来实现我们的plugin方法,通过Plugin类的wrap()。首先通过@Intercepts@Signature注解来解析出我们需要拦截类,然后使用到了动态代理生成个代理对象返回。
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代理生成一个代理类
return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
}
在invoke方法里调用到自定义拦截器的interceptor方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
// 调用拦截器的interceptor方法
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}