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

前辈博文链接:共三篇:

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. 解决ubuntu eclipse中 Android SDK Manager 图标不见的方法

    在eclipse中选择的步骤如下:   Window ---> Custom Perspective  --> command Group Availability ----> an ...

  2. 怎么在GitHub上寻找开源项目呢

    find projects GitHub Explore: Popular and trending projects. GitHub Stars: Projects starred by other ...

  3. Resources.Load加载文件返回null的原因

    1.文件夹都要放在Resources目录下 2.加载时photoName不需要扩展名 Texture2D t = Resources.Load<Texture2D>("Loadi ...

  4. [LeetCode] Reverse Words in a String 翻转字符串中的单词

    Given an input string, reverse the string word by word. For example, Given s = "the sky is blue ...

  5. [LeetCode] Maximal Rectangle 最大矩形

    Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and ...

  6. IEEE 802.11p (WAVE,Wireless Access in the Vehicular Environment)

    IEEE 802.11p(又称WAVE,Wireless Access in the Vehicular Environment)是一个由IEEE 802.11标准扩充的通讯协定.这个通讯协定主要用在 ...

  7. 【转】封装原生JS实现Ajax

    function createXHR() { if (window.XMLHttpRequest) { //IE7+.Firefox.Opera.Chrome 和Safari return new X ...

  8. iOS推送证书转pem文件

    iOS推送证书转 .pem文件. 推送证书转pem文件openssl x509 -in apns_miaobozhibo.cer -inform der -out apns_miaobozhibo.p ...

  9. 因为多余jar包,所报的错

    今天,将项目部署到服务器时,出现了上述的错误.于是就开始找度娘了,找啊找,将项目里面的东西翻来覆去的改,不见效果,还多了些莫名其妙的错,当时就懵逼了.在百度上找到的方法都是说有jar包重复,项目里面的 ...

  10. angualr 实现tab选项卡功能

    tab.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...