struts2拦截器兑现原理(转)

struts2拦截器实现原理(转)
第一步:创建目标对象的接口

/**
* 目标对象的接口 JDK的动态代理要求目标对象必须实现一个接口

* @author 张明学

*/
public interface TargetInter {
    public void doSomething();
}
第二步: 实现接口创建目标对象

/**
* 目标对象

* @author 张明学

*/
public class Target implements TargetInter {

public void doSomething() {
        System.out.println("目标对象的方法执行!");
    }

}

第三步:创建我们的拦截器对象

/**
* 拦截器对象

* @author 张明学

*/
public class Interceptor {
    public void before() {
        System.out.println("before方法执行!");
    }

public void after() {
        System.out.println("after方法执行!");
    }
}

第四步:创建我们的处理器

/**
* 处理器类 JDK代理要求实现java.lang.reflect.InvocationHandler接口

* @author 张明学

*/
public class MyHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

public void setTarget(Object target) {
        this.target = target;
    }

/**
     * 拦截器对象
     */
    private Interceptor interceptor = new Interceptor();

/**
     * invoke为真正执行的方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;

interceptor.before();

result = method.invoke(target, args);

interceptor.after();

return result;
    }

}
第五步:创建代理对象

/**
* 代理对象

* @author 张明学

*/
public class MyProxy {
    /**
     * 得到对象的实例
     * @param obj
     * @return
     */
    public Object getProxy(Object obj) {

MyHandler myHandler = new MyHandler();
        myHandler.setTarget(obj);

/**
         * 第一个参数:得到目标对象的class对象的ClassLoader 
         * 第二个参数:目标对象所实现的接口数组
         * 第三个参数:真正执行的对象(处理器)
         */
        return Proxy.newProxyInstance(Target.class.getClassLoader(), obj
                .getClass().getInterfaces(), myHandler);
    }

}

测试:

public class MyTest {

public static void main(String[] args) {
        TargetInter targer = new Target();
        MyProxy myProxy = new MyProxy();
        /**
         * 得到代理 
         */
        TargetInter proxy = (TargetInter) myProxy.getProxy(targer);

proxy.doSomething();
    }

}

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

www.MyException.Cn   发布于:2012-09-20 09:36:50   浏览:0次

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 的实现,将拦截器由中央调度器统一调度,这样只需一个代理(插件)来启动调度逻辑就行,每次都是调用中央调度器推进责任链的调度,也就是向前推进。

转载于:https://www.cnblogs.com/F-Fly/archive/2013/01/16/2862631.html

AOP 拦截器 JDK动态代理机制 struts2 mybatis spring-aop相关推荐

  1. spring中aop默认使用jdk动态代理,springboot2以后默认使用cglib来实现动态代理详解

    Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...

  2. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer {public Object execute ...

  3. 深度剖析JDK动态代理机制

    摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...

  4. Spring 面向切面编程 第3关:AOP实现原理-JDK动态代理

    目录 任务描述 相关知识 代理模式(Proxy) AOP实现的两种方式 JDK动态代理步骤 案例模拟AOP实现 代理类说明 编程要求 测试说明 参考代码 任务描述 我们知道,Spring AOP的主要 ...

  5. AOP的两种动态代理机制

    从多处拷贝粘贴而来,原文无处可考了,感谢几位原创者的付出 JDK动态代理和Cglib动态代理 写了个小demo,GitHub:https://github.com/ConanDennis/dynami ...

  6. aop实现原理 - JDK动态代理(实例+源码解析)

    动态代理: jdk代理-基于接口代理 通过 类:java.lang.reflect.Proxy 生成动态代理类 实现 接口:InvocationHandler 只能基于接口进行动态代理 代码实现: 1 ...

  7. 带你轻松搞懂JDK动态代理机制(ssm框架先行必备)

    学习动态代理之前需要先搞清楚静态代理,可以回看我之前在Mybatis里写的代理那一篇文章 MyBatis入门基础3 ---代理 动态代理 使用反射机制,在程序执行过程中,创建代理类对象,无需创建类文件 ...

  8. Filter - 通过拦截器与动态代理实现敏感词汇过滤

    需求:将敏感词汇.txt文件中的词语过滤为 -> "***" 分析: 首先加载文档,将文档中的词汇纳入集合中备用,方便后期比对使用. 使用Proxy动态代理将request对 ...

  9. 什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?

    Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 群聊天 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 ...

最新文章

  1. 入门视频采集与处理(学会分析YUV数据)
  2. Python的列表推导式
  3. java注册头像_注册页面头像上传的实现(javaweb相关)
  4. which oracle linux,(总结)Linux下Oracle11gR2的ORA-00845错误解决方法
  5. java wed登录面 代码_JavaWeb实现用户登录注册功能实例代码(基于Servlet+JSP+JavaBean模式)...
  6. 华为Mate 40手机将于国庆节发售:搭载全新5nm芯片
  7. maven java jar_如何去maven仓库下载jar包
  8. 【089】◀▶ Microsoft Office 技巧
  9. LINUX中nagios客户端安装步骤及遇到问题
  10. 明解C语言(入门篇)第十一章
  11. Caffe框架-入门浅谈及碰到的坑
  12. 机器视觉软件能够做什么?-龙熙视觉机器视觉培训李杰
  13. 菜鸟和计算机高手的差别
  14. 程序员如何跳出死循环,不作所谓的“程序猿”
  15. 波浪数,51nod1788,根号分治+Meet in the Middle
  16. 快速转行做产品经理系列基础篇:1、中国互联网发展史简述
  17. 使用Bandwagon服务器ftp解决git clone速度慢的问题
  18. C#入门小练习2储蓄计算器程序
  19. sd3403开发板学习(一)
  20. java考勤系统数据库_java考勤管理系统数据库实现

热门文章

  1. 水仙花数python代码输入一个介于3到5的正整数_Python练习题——自幂数(水仙花数)...
  2. 关于结构体数据的读写
  3. html无需列表怎么打,如何以html无序列表形式而不是xml打印这些结果?
  4. mysql去重的最方便的两种方法_mysql去重的最方便的两种方法
  5. java long类型赋值_Java语言编程第22讲——如何理解“Java是强类型语言”
  6. 授权函php还是提示没权限_大快人心,只需一招,BAT 毒瘤再也不敢滥用权限
  7. CodeVs 1017 乘积最大(DP)
  8. aix 添加lv 大小
  9. SOCK开发之---TCP/IP简介
  10. What we find changes who we become.