AOP 拦截器 JDK动态代理机制 struts2 mybatis spring-aop
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兑现原理比较(一)
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相关推荐
- spring中aop默认使用jdk动态代理,springboot2以后默认使用cglib来实现动态代理详解
Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...
- 利用JDK动态代理机制实现简单拦截器
利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer {public Object execute ...
- 深度剖析JDK动态代理机制
摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过 ...
- Spring 面向切面编程 第3关:AOP实现原理-JDK动态代理
目录 任务描述 相关知识 代理模式(Proxy) AOP实现的两种方式 JDK动态代理步骤 案例模拟AOP实现 代理类说明 编程要求 测试说明 参考代码 任务描述 我们知道,Spring AOP的主要 ...
- AOP的两种动态代理机制
从多处拷贝粘贴而来,原文无处可考了,感谢几位原创者的付出 JDK动态代理和Cglib动态代理 写了个小demo,GitHub:https://github.com/ConanDennis/dynami ...
- aop实现原理 - JDK动态代理(实例+源码解析)
动态代理: jdk代理-基于接口代理 通过 类:java.lang.reflect.Proxy 生成动态代理类 实现 接口:InvocationHandler 只能基于接口进行动态代理 代码实现: 1 ...
- 带你轻松搞懂JDK动态代理机制(ssm框架先行必备)
学习动态代理之前需要先搞清楚静态代理,可以回看我之前在Mybatis里写的代理那一篇文章 MyBatis入门基础3 ---代理 动态代理 使用反射机制,在程序执行过程中,创建代理类对象,无需创建类文件 ...
- Filter - 通过拦截器与动态代理实现敏感词汇过滤
需求:将敏感词汇.txt文件中的词语过滤为 -> "***" 分析: 首先加载文档,将文档中的词汇纳入集合中备用,方便后期比对使用. 使用Proxy动态代理将request对 ...
- 什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?
Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 群聊天 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 ...
最新文章
- 入门视频采集与处理(学会分析YUV数据)
- Python的列表推导式
- java注册头像_注册页面头像上传的实现(javaweb相关)
- which oracle linux,(总结)Linux下Oracle11gR2的ORA-00845错误解决方法
- java wed登录面 代码_JavaWeb实现用户登录注册功能实例代码(基于Servlet+JSP+JavaBean模式)...
- 华为Mate 40手机将于国庆节发售:搭载全新5nm芯片
- maven java jar_如何去maven仓库下载jar包
- 【089】◀▶ Microsoft Office 技巧
- LINUX中nagios客户端安装步骤及遇到问题
- 明解C语言(入门篇)第十一章
- Caffe框架-入门浅谈及碰到的坑
- 机器视觉软件能够做什么?-龙熙视觉机器视觉培训李杰
- 菜鸟和计算机高手的差别
- 程序员如何跳出死循环,不作所谓的“程序猿”
- 波浪数,51nod1788,根号分治+Meet in the Middle
- 快速转行做产品经理系列基础篇:1、中国互联网发展史简述
- 使用Bandwagon服务器ftp解决git clone速度慢的问题
- C#入门小练习2储蓄计算器程序
- sd3403开发板学习(一)
- java考勤系统数据库_java考勤管理系统数据库实现
热门文章
- 水仙花数python代码输入一个介于3到5的正整数_Python练习题——自幂数(水仙花数)...
- 关于结构体数据的读写
- html无需列表怎么打,如何以html无序列表形式而不是xml打印这些结果?
- mysql去重的最方便的两种方法_mysql去重的最方便的两种方法
- java long类型赋值_Java语言编程第22讲——如何理解“Java是强类型语言”
- 授权函php还是提示没权限_大快人心,只需一招,BAT 毒瘤再也不敢滥用权限
- CodeVs 1017 乘积最大(DP)
- aix 添加lv 大小
- SOCK开发之---TCP/IP简介
- What we find changes who we become.