Spring Aop(十五)——Aop原理之Advised接口
Spring Aop原理之Advised接口
通过之前我们介绍的ProxyFactory
我们知道,Spring Aop是通过ProxyFactory
来创建代理对象的。ProxyFactory
在创建代理对象时会委托给DefaultAopProxyFactory.createAopProxy(AdvisedSupport config)
,DefaultAopProxyFactory
内部会分情况返回基于JDK的JdkDynamicAopProxy
或基于CGLIB的ObjenesisCglibAopProxy
,它俩都实现了Spring的AopProxy
接口。AopProxy
接口中只定义了一个方法,getProxy()
方法,Spring Aop创建的代理对象也就是该接口方法的返回结果。
我们先来看一下基于JDK代理的JdkDynamicAopProxy
的getProxy()的逻辑。
@Overridepublic Object getProxy() {return getProxy(ClassUtils.getDefaultClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());}Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}
我们可以看到它最终是通过JDK的Proxy
来创建的代理,使用的InvocationHandler
实现类是它本身,而使用的接口是AopProxyUtils.completeProxiedInterfaces(this.advised)
的返回结果。而这个this.advised
对象是AdvisedSupport
类型,它是ProxyFactory
的父类(间接通过ProxyCreatorSupport
继承,ProxyFactory
的直接父类是ProxyCreatorSupport
,ProxyCreatorSupport
的父类是AdvisedSupport
),AdvisedSupport
的父类是ProxyConfig
,ProxyConfig
中包含创建代理对象时的一些配置项信息。以下是AopProxyUtils.completeProxiedInterfaces(this.advised)
的内部逻辑。
public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();if (specifiedInterfaces.length == 0) {// No user-specified interfaces: check whether target class is an interface.Class<?> targetClass = advised.getTargetClass();if (targetClass != null && targetClass.isInterface()) {specifiedInterfaces = new Class<?>[] {targetClass};}}boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);int nonUserIfcCount = 0;if (addSpringProxy) {nonUserIfcCount++;}if (addAdvised) {nonUserIfcCount++;}Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);if (addSpringProxy) {proxiedInterfaces[specifiedInterfaces.length] = SpringProxy.class;}if (addAdvised) {proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class;}return proxiedInterfaces;}
我们可以看到其会在!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)
返回true
的情况下加上本文的主角Advised
接口。isOpaque()
是ProxyConfig
中的一个方法,对应的是opaque
属性,表示是否禁止将代理对象转换为Advised
对象,默认是false
。!advised.isInterfaceProxied(Advised.class)
表示将要代理的目标对象类没有实现Advised
接口,对于我们自己应用的Class
来说,一般都不会自己去实现Advised
接口的,所以这个通常也是返回true
,所以通常创建Aop代理对象时是会创建包含Advised
接口的代理对象的,即上述的proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class
会被执行。
前面我们已经提到,JdkDynamicAopProxy
创建代理对象应用的InvocationHandler
是其自身,所以我们在调用JdkDynamicAopProxy
创建的代理对象的任何方法时都将调用JdkDynamicAopProxy
实现的InvocationHandler
接口的invoke(Object proxy, Method method, Object[] args)
方法。该方法实现如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Class<?> targetClass = null;Object target = null;try {if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {// The target does not implement the equals(Object) method itself.return equals(args[0]);}if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {// The target does not implement the hashCode() method itself.return hashCode();}if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}Object retVal;if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// May be null. Get as late as possible to minimize the time we "own" the target,// in case it comes from a pool.target = targetSource.getTarget();if (target != null) {targetClass = target.getClass();}// Get the interception chain for this method.List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a MethodInvocation.if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);} else {// We need to create a method invocation...invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}// Massage return value if necessary.Class<?> returnType = method.getReturnType();if (retVal != null && retVal == target && returnType.isInstance(proxy) &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {// Special case: it returned "this" and the return type of the method// is type-compatible. Note that we can't help if the target sets// a reference to itself in another returned object.retVal = proxy;} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);}return retVal;}finally {if (target != null && !targetSource.isStatic()) {// Must have come from TargetSource.targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}
其中关于Advised
接口方法调用最核心的一句是如下这句。我们可以看到,当我们调用的目标方法是定义自Advised
接口时,对应方法的调用将委托给AopUtils.invokeJoinpointUsingReflection(this.advised, method, args)
,invokeJoinpointUsingReflection
方法的逻辑比较简单,是通过Java反射来调用目标方法。在这里invokeJoinpointUsingReflection
传递的目标对象正是AdvisedSupport
类型的this.advised
对象。
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Service invocations on ProxyConfig with the proxy config...return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);}
AdvisedSupport
类是实现了Advised
接口的,所以Spring Aop创建了基于Advised
接口的代理对象后在调用Advised
接口方法时可以把它委托给AdvisedSupport
。而我们知道Spring Aop代理对象的创建正是基于AdvisedSupport
的配置进行的(配置项主要都定义在AdvisedSupport
的父类ProxyConfig
类中)。创建代理对象时应用AdvisedSupport
,调用Advised
接口方法也用同一个实现了Advised
接口的AdvisedSupport
对象,所以这个过程在Spring Aop内部就可以很好的衔接。接着我们来看一下Advised
接口的定义。
public interface Advised extends TargetClassAware {boolean isFrozen();boolean isProxyTargetClass();Class<?>[] getProxiedInterfaces();boolean isInterfaceProxied(Class<?> intf);void setTargetSource(TargetSource targetSource);TargetSource getTargetSource();void setExposeProxy(boolean exposeProxy);boolean isExposeProxy();void setPreFiltered(boolean preFiltered);boolean isPreFiltered();Advisor[] getAdvisors();void addAdvisor(Advisor advisor) throws AopConfigException;void addAdvisor(int pos, Advisor advisor) throws AopConfigException;boolean removeAdvisor(Advisor advisor);void removeAdvisor(int index) throws AopConfigException;int indexOf(Advisor advisor);boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;void addAdvice(Advice advice) throws AopConfigException;void addAdvice(int pos, Advice advice) throws AopConfigException;boolean removeAdvice(Advice advice);int indexOf(Advice advice);String toProxyConfigString();}
Advised
接口中定义的方法还是非常多的,通过它我们可以在运行时了解我们的代理对象是基于CGLIB的还是基于JDK代理的;可以了解我们的代理对应应用了哪些Advisor
;也可以在运行时给我们的代理对象添加和删除Advisor/Advise
。本文旨在描述Spring Aop在创建代理对象时是如何基于Advised
接口创建代理的,以及我们能够应用Advised
接口做哪些事。文中应用的是Spring创建基于JDK代理对象的过程为示例讲解的,其实基于CGLIB的代理也是一样的。关于CGLIB的代理过程、本文中描述的一些核心类以及本文的核心——Advised
接口的接口方法说明等请有兴趣的朋友参考Spring的API文档和相关的源代码。
(注:本文是基于Spring4.1.0所写,Elim写于2017年5月15日)
Spring Aop(十五)——Aop原理之Advised接口相关推荐
- Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例
<p>这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例.</p> 先和大家聊聊我为什么喜欢写这种脚手架的项目,在我学习一门新技术的时候,总是想快速的搭 ...
- 让大学生校招逆袭的Spring三十五问,四万字详解分析(堪称2022年大学生毕业面试指南)
有人说,"Java程序员都是Spring程序员",我不太赞成这个观点,但是这也可以看出Spring在Java世界里举足轻重的作用. 基础 1.Spring是什么?特性?有哪些模块? ...
- 面渣逆袭:Spring三十五问,四万字+五十图详解
基础 1.Spring是什么?特性?有哪些模块? 一句话概括:Spring 是一个轻量级.非入侵式的控制反转 (IoC) 和面向切面 (AOP) 的框架. 2003年,一个音乐家Rod Johnson ...
- 面渣逆袭:Spring三十五问,四万字+五十图详解 。不要错过
基础 1.Spring是什么?特性?有哪些模块? Spring Logo 一句话概括:Spring 是一个轻量级.非入侵式的控制反转 (IoC) 和面向切面 (AOP) 的框架. 2003年,一个音乐 ...
- 面渣逆袭:Spring三十五问,四万字+五十图详解,建议收藏。
这节我们来搞定另一个面试必问知识点--Spring. 有人说,"Java程序员都是Spring程序员",老三不太赞成这个观点,但是这也可以看出Spring在Java世界里举足轻重的 ...
- java 分布式事务_Java核心知识 Spring原理十五 JPA 原理
1. 事务 事务是计算机应用中不可或缺的组件模型,它保证了用户操作的原子性 ( Atomicity ).一致性 ( Consistency ).隔离性 ( Isolation ) 和持久性 ( Dur ...
- Spring三十五问,四万字+五十图详解 建议收藏
这节我们来搞定另一个面试必问知识点--Spring. 有人说,"Java程序员都是Spring程序员",老三不太赞成这个观点,但是这也可以看出Spring在Java世界里举足轻重的 ...
- (转)Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例
http://www.ityouknow.com/springboot/2017/09/23/spring-boot-jpa-thymeleaf-curd.html 这篇文章介绍如何使用 Jpa 和 ...
- spring boot(十五)spring boot+thymeleaf+jpa增删改查示例
快速上手 配置文件 pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 <dependency><groupId>org.springframework.bo ...
- 【零基础学Java】—继承父类并实现多个接口(二十五)
[零基础学Java]-继承父类并实现多个接口(二十五) 一.使用接口的注意事项 1️⃣接口是没有静态代码块或者构造方法的 2️⃣一个类的直接父类是唯一的,但是一个类可以同时实现多个接口 3️⃣如果实现 ...
最新文章
- 更改centos 5 yum源
- MATLAB应用实战系列(七十七)-【图像处理】COVID-19 防疫应用口罩检测
- 04-CoreData Stack技术栈堆手动实现
- Java集合篇:集合细节:为集合指定初始容量、asList的缺陷、subList的缺陷
- Plupload+easyui+springmvc实现批量上传
- 字符串匹配的Boyer-Moore算法
- MySQL Hardware--FIO压测
- Jquery Datatable的使用样例(ssm+bootstrsp框架下)服务器端分页
- iisS7 配置SSL 绑定主机头实现多站点访问
- centos 程序 mysql_Centos 源码安装 MySQL
- q语言 科学计数_3岁宝宝说话结巴,被诊断语言障碍,我用1招让孩子口齿清晰,打脸众人!...
- c语言如何调用外部文件的函数调用,keil 中如何调用其他文件的函数
- 组内连续三个或三个以上Repeated Measures ANOVA
- vue json对象转数组_年薪百万之路--第六十七天 Vue入门
- 软件测试工具常用的都有哪些?
- 请教一个能在WinPE环境下获取系统相关信息的代码
- matlab神经网络应用设计 张德丰,MATLAB神经网络应用设计
- 五一劳动节,微信公众号图文应该怎样排版?
- 开发 HTML5 小游戏
- 银联商务技术开发笔试题目
热门文章
- [原创]windows server 2012 AD架构 试验 系列 – 17管理用户AD帐号
- linux没有cpufreq目录,Linux内核的cpufreq(变频)机制
- windows 截图软件——sharex 截图软件的天花板 并且是免费开源的。
- 华为智慧屏鸿蒙20,华为智慧屏SE系列首销 搭载鸿蒙分布式跨屏技术
- 《文明的冲突与世界秩序的重建》摘
- 内存屏障(Memory Barrier)(一)什么是写屏障?
- macos可以升级到指定版本吗_错过Mac OS升级,找不到旧OS版本怎么办?
- Tesseract-OCR样本训练方法
- Android查电池循环软件,这款软件,让安卓也能查看电池寿命
- 解决iText 5.0.1,加入iTextAsian.jar 出现异常 Font 'STSong-Light' with 'UniGB-UCS2-H'