网上一搜,发现一篇写的非常棒的博文,就直接复制过来了,供以后复习使用。

前辈博文链接:共三篇:

http://jimgreat.iteye.com/blog/1616671;

http://jimgreat.iteye.com/blog/1594981;

http://jimgreat.iteye.com/blog/1594982;

以下为第一篇:

其实无论是AOP、拦截器还是Plugin 都是通过对目标点,一般来说就是对函数的拦截,扩展原有的功能,增加切面逻辑(日志,权限验证),修改上下文运行数据(实现Mybatis物理分页)。

Spring-AOP是个通用的框架,通过配置可以对任意函数进行拦截

Struts2是Web框架,它的拦截器就只针对它的Action

Mybatis的Plugin是针对它封装的JDBC各个环节进行拦截(http://www.mybatis.org/core/configuration.html#plugins)

注:中文的拦截器、通知、插件指的都是拦截器

实现原理上看,都是通过Java的动态代理机制,在运行时加载拦截器(按AOP的规范也叫通知器),对目标对象生成代理,在特定接口对应的函数调用时,实施拦截,调用拦截器的逻辑。

Spring-AOP和Struts2都是将多个拦截器组织到数组中,在每个拦截方法调用时以责任链的形式,会有一个中央调度器,触发下一个拦截器。

这里面,Struts2需要拦截器在实现时来组织调用逻辑,比如是在目标对象前还是后来执行拦截的逻辑。而Spring-AOP对不同的拦截器又进行了细分,有BeforeAdvice、AfterreturningAdvice、AroundAdvice在Spring中叫通知,会和Pointcut切点结合生成Advisor通知器,最后通过相应的适配器都会转成拦截器。

Spring-AOP中的Pointcut可以看成是对拦截点过滤机制的一种抽象和对象化表示形式,也就是指定在哪些类和哪些方法上进行拦截。Struts2也有类似的机制,但过滤的只是Action中的方法。

Mybatis也是责任链,动态代理,可过滤拦截点,和Spring-AOP、Struts2有个理念上的差别是,它在组织多个拦截器时使用的是层层代理,就是第一个插件代理目标实例 、第二个插件再生成第一个代理的代理、第三个插件再生成第二个代理的代理......    这个真是原生AOP呀!!!

好,下面一一分析。

MyBatis

我们先看一下这个层层代理是怎么生成的

下面是一个 Mybatis Plugin 的简单例子

1、函数上的注解是指定拦截方法的签名  [type,method,args]

2、Object intercept(Invocation invocation)  是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器或拦截的目标方法。

3、Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target, this) 来完成的,把目标target和拦截器this传给了包装函数。

  1. // ExamplePlugin.java
  2. @Intercepts({@Signature(
  3. type= Executor.class,
  4. method = "update",
  5. args = {MappedStatement.class,Object.class})})
  6. public class ExamplePlugin implements Interceptor {
  7. public Object intercept(Invocation invocation) throws Throwable {
  8. return invocation.proceed();
  9. }
  10. public Object plugin(Object target) {
  11. return Plugin.wrap(target, this);
  12. }
  13. public void setProperties(Properties properties) {
  14. }
  15. }

在下面的代码中可以看出   Plugin.wrap 从拦截器中取出拦截点方法签并生成对应的接口类,再通过Proxy生成代理对象。这个代理的InvocationHandler就是Plugin,里面封装了target, interceptor, signatureMap,并实现invoke方法,后面会分析。

  1. public static Object wrap(Object target, Interceptor interceptor) {
  2. Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  3. Class<?> type = target.getClass();
  4. Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  5. if (interfaces.length > 0) {
  6. return Proxy.newProxyInstance(
  7. type.getClassLoader(),
  8. interfaces,
  9. new Plugin(target, interceptor, signatureMap));
  10. }
  11. return target;

Mybatis的插件是针对它封装的处理类进行拦截的。这些处理类都是在org.apache.ibatis.session.Configuration中生成的,在下面这些生成函数中,都调用了 interceptorChain.pluginAll 对目标处理类附加拦截器。

  1. public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  2. ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  3. <strong> </strong>parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  4. return parameterHandler;
  5. }
  6. public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
  7. ResultHandler resultHandler, BoundSql boundSql) {
  8. ResultSetHandler resultSetHandler = mappedStatement.hasNestedResultMaps() ? new NestedResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql,
  9. rowBounds) : new FastResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
  10. <strong> </strong> resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  11. return resultSetHandler;
  12. }
  13. public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  14. StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  15. <strong>  </strong>statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  16. return statementHandler;
  17. }
  1. public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
  2. ......
  3. executor = (Executor) interceptorChain.pluginAll(executor);
  4. return executor;
  5. }

我们看一下这个pluginAll做了什么:

  1. public Object pluginAll(Object target) {
  2. <strong> </strong> for (Interceptor interceptor : interceptors) {
  3. target = interceptor.plugin(target);
  4. }
  5. return target;
  6. }

遍历拦截器,调用拦截器的plugin,把拦截器附加到target上。第一次执行后,这个target就变成了原始处理类实例的代理,到最后这个target就变成被拦截器层层代理的代理实例了。

就是这个for实现了前面说的层层代理 【第一个插件代理目标实例 、第二个插件再生成第一个代理的代理、第三个插件再生成第二个代理的代理......】

下面说一下代理入口和责任链的推进

每个代理的InvocationHandler都是org.apache.ibatis.plugin.Plugin类,它的invoke方法也是代理执行的入口

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  2. try {
  3. Set<Method> methods = signatureMap.get(method.getDeclaringClass());
  4. if (methods != null && methods.contains(method)) {
  5. return interceptor.intercept(new Invocation(target, method, args));
  6. }
  7. return method.invoke(target, args);
  8. } catch (Exception e) {
  9. throw ExceptionUtil.unwrapThrowable(e);
  10. }
  11. }

在invoke里,如果方法签名和拦截中的签名一致,就调用拦截方法,并将下一个目标target(如果有多个拦截器,就是一下个代理)、拦截的method和arg 封装到Invocation中,传给下一个拦截器。

invocation.proceed()就是简单调用下一个target的对应方法,如果一下个还是代理,就由回到上面的invoke方法了。

这里就解释了上面说的 【Object intercept(Invocation invocation)  是实例拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器或拦截的目标方法。】

  1. public Object proceed() throws InvocationTargetException, IllegalAccessException {
  2. return method.invoke(target, args);
  3. }

总结:

 

我们假设在MyBatis配置了一个插件,在运行时会发生什么?

1、所有可能被拦截的处理类都会生成一个代理

2、处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法

3、执行插接中的拦截方法后,推进目标的执行

如果有N个插件,就有N个代理,每个代理都要执行上面的逻辑

这里面的层层代理要多次生成动态代理,是比较影响性能的。虽然能指定插件拦截的位置,但这个是在执行方法时动态判断,初始化的时候就是简单的把插件包装到了所有可以拦截的地方。

不过一般来说使用MyBatis也不会用很多插件,也可能是因为这个原因,它的拦截机制实现的不是很精细。如果实现情况中一定要有好多插件,我认为可以参照下面Struts2 和 Spring-AOP 的实现,将拦截器由中央调度器统一调度,这样只需一个代理(插件)来启动调度逻辑就行,每次都是调用中央调度器推进责任链的调度,也就是向前推进。

以上。

以简单的话来说:mybatis是按顺序,以target为核心,包含关系的产生一个又一个的代理对象;

struts2是将所有的过滤器放在一个集合里,然后遍历集合,期间,每次都对请求做加工处理。

毫无疑问,mybatis加载插件的方式效率更低。

关于struts2的过滤器和mybatis的插件的分析的更多相关文章

  1. Mybatis分页插件

    mybatis配置 <!-- mybatis分页插件 --> <bean id="pagehelper" class="com.github.pageh ...

  2. Myeclipse2014添加mybatis generator插件

    Myeclipse2014把mybatis generator插件直接放在dropins文件夹下,重启后不能成功安装mybatis插件. 既然离线安装不成功,可以选择在线安装 1.选择 Help-&g ...

  3. mybatis分页插件以及懒加载

    1.   延迟加载 延迟加载的意义在于,虽然是关联查询,但不是及时将关联的数据查询出来,而且在需要的时候进行查询. 开启延迟加载: <setting name="lazyLoading ...

  4. mybatis generator 插件安装及使用

    现在Mybatis特别火,但是在开发中却要经常写实体类和配置文件,会不会特别烦人,所以可以利用Mybatis的代码生成插件来生成这部分代码: 1,打开eclipse,点击Help>Softwar ...

  5. Mybatis分页插件PageHelper的配置和使用方法

     Mybatis分页插件PageHelper的配置和使用方法 前言 在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页. 前端分 ...

  6. Mybatis分页插件PageHelper使用

    一. Mybatis分页插件PageHelper使用  1.不使用插件如何分页: 使用mybatis实现: 1)接口: List<Student> selectStudent(Map< ...

  7. SSM 使用 mybatis 分页插件 pagehepler 实现分页

    使用分页插件的原因,简化了sql代码的写法,实现较好的物理分页,比写一段完整的分页sql代码,也能减少了误差性. Mybatis分页插件 demo 项目地址:https://gitee.com/fre ...

  8. Mybatis插件原理分析(三)分页插件

    在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件. 虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBo ...

  9. Mybatis插件原理分析(二)

    在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析. ...

随机推荐

  1. Windows Server 2012 虚拟化实战:网络(一)

    虚拟化对于计算的抽象,大家可能相对熟悉,也许都有在单机使用诸如Virtual PC或者Virtual Box的经验.使用的这些虚拟化软件的第一印象就是我们的CPU可以同时运行多套不同的操作系统,并且其 ...

  2. Linux 信号(一)—— kill 函数

    世事并无好坏之分,全看我们怎么去想.—— 哈姆雷特·第二幕第二景 ilocker:关注 Android 安全(新入行,0基础) QQ: 2597294287 #include <signal.h ...

  3. 《Note --- Unreal 4 --- matinee》

    https://docs.unrealengine.com/latest/CHN/Engine/Matinee/index.html https://docs.unrealengine.com/lat ...

  4. Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译

    本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  5. 用ORM的思想操作XML文档,一个对象就搞定不要太简单。滚蛋吧!XmlDocument、XmlNode、Xml***……

    大家有没有这样的感受,一涉及XML文档操作就得百度一遍.是不是非!常!烦!.各种类型,各种方法,更别提为了找到一个节点多费劲.本来想写个XML操作的工具方法,写了两行一想既然XML文档是有规律的,如果 ...

  6. HDOJ 2317. Nasty Hacks 模拟水题

    Nasty Hacks Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tota ...

  7. 如何搭建SVN服务器,详细安装步骤。

    SVN服务器端安装 下载: VisualSVN是一款图形化svn服务器.官网 http://www.visualsvn.com/server/ 下载地址: http://www.visualsvn.c ...

  8. ArrayList<E>源码分析

    ArrayList是按照线性表结构实现的 ArrayList的主要继承结构 public class ArrayList<E> extends AbstractList<E> ...

  9. Linux文本查看及处理.md

    cat cat命令的用途是连接文件或标准输入并打印.这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用. 主要功能 一次显示整个文件:cat ...

  10. font-size 兼容问题

    早年~ 楔子 在为“我的抵扣券”添加  按钮时,为了将文字隐掉,给节点设置了“font-size:0;”,设置后刷一下浏览器,webkit下按钮掉下去了,而其他浏览器(包括IE6/7)都正常: 按理说 ...