博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis Interceptor 拦截器原理 源码分析
阅读量:5918 次
发布时间:2019-06-19

本文共 7636 字,大约阅读时间需要 25 分钟。

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。

代理链的生成

Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。

 

通过查看Configuration类的源代码我们可以看到,每次都对目标对象进行代理链的生成。

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {    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) {    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, ExecutorType executorType, boolean autoCommit) {    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, autoCommit);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }
复制代码

接下来让我们通过分析源代码的方式来解读Mybatis的拦截器实现原理

对于拦截器Mybatis为我们提供了一个Interceptor接口,通过实现该接口就可以定义我们自己的拦截器。我们先来看一下这个接口的定义:

 

复制代码
1 package org.apache.ibatis.plugin; 2  3 import java.util.Properties; 4  5 public interface Interceptor { 6  7   Object intercept(Invocation invocation) throws Throwable; 8  9   Object plugin(Object target);10 11   void setProperties(Properties properties);12 13 }
复制代码

 

 

我们可以看到在该接口中一共定义有三个方法,intercept、plugin和setProperties。plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法,当然也可以调用其他方法,这点将在后文讲解。setProperties方法是用于在Mybatis配置文件中指定一些属性的。

定义自己的Interceptor最重要的是要实现plugin方法和intercept方法,在plugin方法中我们可以决定是否要进行拦截进而决定要返回一个什么样的目标对象。而intercept方法就是要进行拦截的时候要执行的方法。

对于plugin方法而言,其实Mybatis已经为我们提供了一个实现。Mybatis中有一个叫做Plugin的类,里面有一个静态方法wrap(Object target,Interceptor interceptor),通过该方法可以决定要返回的对象是目标对象还是对应的代理。这里我们先来看一下Plugin的源码:

复制代码
1 package org.apache.ibatis.plugin;  2   3 import java.lang.reflect.InvocationHandler;  4 import java.lang.reflect.Method;  5 import java.lang.reflect.Proxy;  6 import java.util.HashMap;  7 import java.util.HashSet;  8 import java.util.Map;  9 import java.util.Set; 10  11 import org.apache.ibatis.reflection.ExceptionUtil; 12  13 //这个类是Mybatis拦截器的核心,大家可以看到该类继承了InvocationHandler 14 //又是JDK动态代理机制 15 public class Plugin implements InvocationHandler { 16  17   //目标对象 18   private Object target; 19   //拦截器 20   private Interceptor interceptor; 21   //记录需要被拦截的类与方法 22   private Map
, Set
> signatureMap; 23 24 private Plugin(Object target, Interceptor interceptor, Map
, Set
> signatureMap) { 25 this.target = target; 26 this.interceptor = interceptor; 27 this.signatureMap = signatureMap; 28 } 29 30 //一个静态方法,对一个目标对象进行包装,生成代理类。 31 public static Object wrap(Object target, Interceptor interceptor) { 32 //首先根据interceptor上面定义的注解 获取需要拦截的信息 33 Map
, Set
> signatureMap = getSignatureMap(interceptor); 34 //目标对象的Class 35 Class
type = target.getClass(); 36 //返回需要拦截的接口信息 37 Class
[] interfaces = getAllInterfaces(type, signatureMap); 38 //如果长度为>0 则返回代理类 否则不做处理 39 if (interfaces.length > 0) { 40 return Proxy.newProxyInstance( 41 type.getClassLoader(), 42 interfaces, 43 new Plugin(target, interceptor, signatureMap)); 44 } 45 return target; 46 } 47 48 //代理对象每次调用的方法 49 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 50 try { 51 //通过method参数定义的类 去signatureMap当中查询需要拦截的方法集合 52 Set
methods = signatureMap.get(method.getDeclaringClass()); 53 //判断是否需要拦截 54 if (methods != null && methods.contains(method)) { 55 return interceptor.intercept(new Invocation(target, method, args)); 56 } 57 //不拦截 直接通过目标对象调用方法 58 return method.invoke(target, args); 59 } catch (Exception e) { 60 throw ExceptionUtil.unwrapThrowable(e); 61 } 62 } 63 64 //根据拦截器接口(Interceptor)实现类上面的注解获取相关信息 65 private static Map
, Set
> getSignatureMap(Interceptor interceptor) { 66 //获取注解信息 67 Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); 68 //为空则抛出异常 69 if (interceptsAnnotation == null) { // issue #251 70 throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); 71 } 72 //获得Signature注解信息 73 Signature[] sigs = interceptsAnnotation.value(); 74 Map
, Set
> signatureMap = new HashMap
, Set
>(); 75 //循环注解信息 76 for (Signature sig : sigs) { 77 //根据Signature注解定义的type信息去signatureMap当中查询需要拦截方法的集合 78 Set
methods = signatureMap.get(sig.type()); 79 //第一次肯定为null 就创建一个并放入signatureMap 80 if (methods == null) { 81 methods = new HashSet
(); 82 signatureMap.put(sig.type(), methods); 83 } 84 try { 85 //找到sig.type当中定义的方法 并加入到集合 86 Method method = sig.type().getMethod(sig.method(), sig.args()); 87 methods.add(method); 88 } catch (NoSuchMethodException e) { 89 throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e); 90 } 91 } 92 return signatureMap; 93 } 94 95 //根据对象类型与signatureMap获取接口信息 96 private static Class
[] getAllInterfaces(Class
type, Map
, Set
> signatureMap) { 97 Set
> interfaces = new HashSet
>(); 98 //循环type类型的接口信息 如果该类型存在与signatureMap当中则加入到set当中去 99 while (type != null) {100 for (Class
c : type.getInterfaces()) {101 if (signatureMap.containsKey(c)) {102 interfaces.add(c);103 }104 }105 type = type.getSuperclass();106 }107 //转换为数组返回108 return interfaces.toArray(new Class
[interfaces.size()]);109 }110 111 }
复制代码
Plugin源代码分析

 

下面是俩个注解类的定义源码

复制代码
1 package org.apache.ibatis.plugin; 2  3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7  8 @Retention(RetentionPolicy.RUNTIME) 9 @Target(ElementType.TYPE)10 public @interface Intercepts {11   Signature[] value();12 }
复制代码
复制代码
1 package org.apache.ibatis.plugin; 2  3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7  8 @Retention(RetentionPolicy.RUNTIME) 9 @Target(ElementType.TYPE)10 public @interface Signature {11   Class
type();12 13 String method();14 15 Class
[] args();16 }
复制代码

http://www.cnblogs.com/daxin/p/3541922.html

 

转载于:https://www.cnblogs.com/softidea/p/6049190.html

你可能感兴趣的文章
Android开发之 Android 的基本组件的概述
查看>>
泛型的使用
查看>>
appium简明教程
查看>>
Tomcat常用的优化技巧
查看>>
史上最详细的Android Studio系列教程四--Gradle基础
查看>>
Android自定义照相机实现(拍照、保存到SD卡,利用Bundle在Acitivity交换数据)
查看>>
iOS 网络错误-分类
查看>>
Wind River Linux 6 Security Profile
查看>>
JS魔法堂:再识Bitwise Operation & Bitwise Shift
查看>>
windows多线程详解
查看>>
JUnit + Mockito 单元测试(二)(good)
查看>>
E. Riding in a Lift(Codeforces Round #274)
查看>>
Android(Xamarin)之旅(四)
查看>>
Markdown 写作工具选择
查看>>
c++ char_traits模板类的实现!!!
查看>>
Drupal 7 建站学习手记(四):怎样改动Nivo Slider模块的宽高
查看>>
SOUI更新到2.0
查看>>
[PCL]5 ICP算法进行点云匹配
查看>>
Python2.7字符编码详解
查看>>
jsonUtil 工具类
查看>>