mybatis 拦截器实现 源码解析

首先自定义拦截器要实现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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值