假设有个需求需要给StatementHandler的update增加点一样的拦截逻辑
@Intercepts( {@Signature( type= Executor.class, method = "update", args = {MappedStatement.class ,Object.class})}, {@Signature( type= StatementHandler.class, method = "update", args = {Statement.class})} ) public class ExamplePlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("before execute update"); Object returnObject = invocation.proceed(); System.out.println("after execute update"); return returnObject; } }同样是获取执行器的逻辑,这个时候我们得到的代理后的对象不会是同时实现了Executor 和StatementHandler接口的,只能实现Executor接口,因为这个
// 这个时候type为 CachingExecutor private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { Set<Class<?>> interfaces = new HashSet<>(); while (type != null) { // type的接口Executor显然是在Map中的 for (Class<?> c : type.getInterfaces()) { // 仅允许对接口为Executor的实例做代理,因为代理后的类一定得是一个Executor的实现类 if (signatureMap.containsKey(c)) { interfaces.add(c); } } type = type.getSuperclass(); } // 最终也就只能找到Executor这么一个接口了 return interfaces.toArray(new Class<?>[interfaces.size()]); }这种情况下就和上面一样了
区别在于,如果我们需要创建一个StatementHandler的实例时,代理对象就会变成StatementHandler的实现类实例了
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) interceptorChain.pluginAll(statementHandler); return statementHandler; } Case 3 多拦截器单拦截方法如果有多个拦截器呢?
@Intercepts( {@Signature( type= Executor.class, method = "update", args = {MappedStatement.class ,Object.class})} ) public class ExamplePluginA implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("before execute update"); Object returnObject = invocation.proceed(); System.out.println("after execute update"); return returnObject; } } @Intercepts( {@Signature( type= Executor.class, method = "flushStatements", args = {})} ) public class ExamplePluginB implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("before execute flushStatements"); Object returnObject = invocation.proceed(); System.out.println("after execute flushStatements"); return returnObject; } }上面有两个拦截器,会出现代理对象成为委托对象的情况,也就是对一个已经是代理对象的对象,再去为它生成代理对象。
假设一开始生成了一个代理类类名为$Proxy0,在InterceptorChain#pluginAll方法中,会将这个代理类作为委托类继续生成一个新的代理类,第一个代理类会执行ExamplePluginA拦截逻辑,第二个代理类会执行ExamplePluginB的拦截逻辑。
从pluginAll方法中可以看到,位于拦截器链末尾的,也可以说是最后被创建的那个代理类,是最先被调用的,因为实际返回给客户端使用的,就是它。
假设第二个代理类类名为$Proxy1,那么看一下这种情况下方法的调用栈是啥样的
总结这里主要说一下mybatis为什么这样实现?
为什么不采用过滤器链的实现方式?
过滤器链的方式,在这里并不怎么适用,虽然也许可以,个人观点觉得,过滤器链更多的是用于过滤,它是对调用请求进行条件判断,是否满足某种条件进而是否具备某个继续执行的权限。它决定的是,调用是否可以继续进行下去。
而拦截器,更多的是拦截调用请求,对拦截后的请求做一些额外的附加逻辑,代理其实就是这种方式,它是一种对原有逻辑的增强,不会改变最初调用的目的地,无论做了多少层的拦截处理,最终都会到达它的目的地(发生异常除外)
如果用过滤器,需要多个过滤器链,并且每个类中都需要持有对其过滤器链的引用等等,用我浅薄的知识稍微想了下,并非不可,实而太麻烦了。最优选择还是动态代理。
为什么要做封装?
回想一下我们直接使用Jdk的动态代理,自己动手实现InvocationHandler,写完就没了,也就是说就做了一次增强。