`
jimgreat
  • 浏览: 132125 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring-AOP、Struts2拦截器、MyBatis Plugin实现原理比较(一)

阅读更多


大部分人看到标题一定会有这样的疑问,"为什么把这个三个看起来不相关的东西放在一起比较呀?"

 

其实无论是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传给了包装函数。

 

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

 

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

 

 

   public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
 

 

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

 

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = new DefaultParameterHandler(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 = mappedStatement.hasNestedResultMaps() ? new NestedResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql,
        rowBounds) : new FastResultSetHandler(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) {
    ......
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

 

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

 

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

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

 

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

 

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

 

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

 

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

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

 

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

 

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

 

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

总结:


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

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

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

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

 

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

 

 

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

 

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

 

 

 

分享到:
评论

相关推荐

    Spring-Struts2-mybatis jar包整合

    - **配置Struts2**:在项目中添加`struts2-core.jar`等必需库,创建`struts.xml`配置文件,定义Action、结果类型、拦截器栈等。Struts2通过Action类处理请求,并转发到相应的视图或执行业务逻辑。 - **Struts2与...

    整合struts2-spring-MyBatis

    5. **整合Struts2和Spring**:使用Spring插件`struts2-spring-plugin`,配置Struts2的拦截器链,使Struts2能够从Spring容器中自动注入Action。 6. **整合Spring和MyBatis**:通过Spring的`SqlSessionFactoryBean`...

    ssm jar spring struts mybatis 所需jar

    其核心JAR文件如`struts2-core`提供了Action、拦截器、结果类型等关键组件。此外,可能还需要其他相关库,如`struts2-convention-plugin`、`struts2-json-plugin`等,用于不同类型的请求处理和数据格式化。 **...

    全注解,演示Struts2 + Spring + MyBatis整合

    **Struts2** 是一个强大的MVC框架,它提供了丰富的拦截器、结果类型和插件,使得Action控制更加简洁。在Struts2中,我们可以使用注解来定义Action类和方法,比如`@Action` 和 `@Result`,这使得配置文件大大简化。 ...

    struts2+spring+mybatis框架JAR包

    Struts2的核心特性包括动作映射、拦截器、结果类型、国际化和主题支持等。它的优点在于提供了丰富的插件和强大的表单验证机制,使得视图层与业务逻辑层解耦,便于维护和扩展。 Spring框架则是一个全面的企业级应用...

    struts2,spring3,mybatis整合图书

    Struts2通过拦截器机制实现了灵活的扩展性,同时支持多种结果类型,如JSP、FreeMarker等。在图书管理系统中,Struts2可以作为用户交互的入口,处理借书、还书、查询等操作的请求。 2. **Spring3**:Spring是一个...

    struts2 spring3 mybatis3整合简单例子

    在实际的项目中,还需要考虑异常处理、国际化、权限控制等方面,Struts2提供了拦截器(Interceptor)机制,可以方便地实现这些功能。Spring的AOP则可以在不修改原有代码的情况下,对代码进行扩展或增强。 总的来说...

    Struts2+Spring3+MyBatis框架整合源码+pdf整合文档

    Struts2的核心是Action和Interceptor(拦截器),通过配置Action和Interceptor,我们可以实现灵活的控制流程和业务处理。 Spring则是一个全面的企业级应用框架,它提供依赖注入(DI)和面向切面编程(AOP)等功能,...

    ssi(struts+spring+maven+mybatis)整合

    通过Action类和配置文件,Struts能够协调用户的输入和系统的响应,提供了丰富的拦截器和插件机制,便于扩展和定制。 **2. Spring:**Spring是一个全面的后端开发框架,包括IOC(Inversion of Control)容器、AOP...

    mybatis+spring+struts必须的jar包

    这些jar包包括但不限于:mybatis、mybatis-spring、spring-core、spring-context、spring-web、spring-tx、struts2-core、struts2-spring-plugin等。此外,还需要配置相应的XML配置文件,如Spring的...

    mybatis拦截器的完整实现

    MyBatis使用了Java的动态代理和AOP(面向切面编程)思想,拦截器实际上是一个实现了`org.apache.ibatis.plugin.Interceptor`接口的类。当MyBatis执行Mapper的方法时,会通过拦截器链对方法进行处理。每个拦截器都有`...

    struts2+spring+mybatis整合

    Struts2、Spring和MyBatis是Java Web开发中三个非常重要的开源框架,它们的整合为开发者提供了强大的功能,能够实现MVC(Model-View-Controller)架构模式,提高开发效率,同时也便于项目的维护和扩展。以下是关于...

    mybatis使用拦截器实现分页操作

    在MyBatis中,我们可以通过实现`org.apache.ibatis.plugin.Interceptor`接口并重写`intercept`方法来创建一个自定义拦截器。 分页是数据库操作中常见的需求,传统的做法是在每个查询方法中添加分页逻辑,但这会导致...

    spring3.0整合mybatis3.0、struts2

    本项目“spring3.0整合mybatis3.0、struts2”正是这样的一个尝试,旨在利用这三个主流的Java技术栈来搭建一个强大的后端架构。接下来,我们将详细探讨这三个框架各自的功能、整合的意义以及如何进行整合。 **Spring...

    SSH整合的包 Struts2包 spring的jar包 Mybatis包

    Struts2-Spring-plugin是一个关键的jar包(如struts2-spring-plugin-2.5.13.jar),它实现了Struts2和Spring之间的无缝集成。这个插件使得Action类可以直接从Spring容器中获取依赖,简化了配置,并且提供了更好的...

    spring_struts2_mybatis配置文件架包

    Spring、Struts2和MyBatis是三个非常著名的Java开源框架,它们分别负责不同的职责:Spring作为全能的轻量级框架,Struts2作为MVC(模型-视图-控制器)框架处理HTTP请求,而MyBatis则是一个强大的持久层框架。...

    Spring与Struts 2整合.zip

    5. **拦截器集成**:可以使用Spring的AOP拦截器与Struts 2的拦截器相结合,增强应用的功能。 6. **测试与调试**:整合完成后,进行单元测试和集成测试,确保所有组件协同工作。 整合Spring和Struts 2可以使开发...

    mybatis-3.5.9 源码(mybatis-3-mybatis-3.5.9.tar.gz)

    7. **插件支持**:MyBatis 提供了插件机制,允许用户自定义拦截器来修改 SQL 执行过程。在源码中,你可以看到 Plugin 类的实现,它是如何实现 AOP 功能的。 8. **事务管理**:MyBatis 支持手动和自动的事务管理。...

    Spring+struts2

    7. **拦截器(Interceptor)**:Struts2 的拦截器可以实现登录检查、权限控制等功能。 8. **过滤器(Filter)**:可能包括字符编码过滤器、Spring Security 过滤器等,用于处理请求和响应。 在实际开发中,还需要...

    struts2+spring3+mybatis整合

    Struts2、Spring3和MyBatis是Java Web开发中常用的三大框架,它们各自负责不同的职责,协同工作可以构建出高效、松耦合的应用。在Java企业级开发中,这三者的整合是常见的实践,通常被称为SSM框架。下面将详细介绍这...

Global site tag (gtag.js) - Google Analytics