Spring Aop(十五)——Aop原理之Advised接口
转发地址:https://www.iteye.com/blog/elim-2398726
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()的逻辑。
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
} @Override
public 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 学习十五 AOP
http://www.hongyanliren.com/2014m12/22797.html 1: 通知(advice): 就是你想要的功能,也就是安全.事物.日子等.先定义好,在想用的地方用一下.包 ...
- Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例
Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例 一.快速上手 1,配置文件 (1)pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 ...
- Java开发学习(十五)----AOP入门案例及其工作流程解析
一.AOP简介 1.1 什么是AOP AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构. OOP(Object Oriented ...
- 面渣逆袭:Spring三十五问,四万字+五十图详解
大家好,我是老三啊,面渣逆袭 继续,这节我们来搞定另一个面试必问知识点--Spring. 有人说,"Java程序员都是Spring程序员",老三不太赞成这个观点,但是这也可以看出S ...
- Spring Boot2(十五):Shiro记住我rememberMe、验证码Kaptcha
接着上次学习的<Spring Boot2(十二):手摸手教你搭建Shiro安全框架>,实现了Shiro的认证和授权.今天继续在这个基础上学习Shiro实现功能记住我rememberMe,以 ...
- Spring学习(十五)----- Spring AOP通知实例 – Advice
Spring AOP(面向方面编程)框架,用于在模块化方面的横切关注点.简单得说,它只是一个拦截器拦截一些过程,例如,当一个方法执行,Spring AOP 可以劫持一个执行的方法,在方法执行之前或之后 ...
- Spring学习十五----------Spring AOP API的Pointcut、advice及 ProxyFactoryBean相关内容
© 版权声明:本文为博主原创文章,转载请注明出处 实例: 1.项目结构 2.pom.xml <project xmlns="http://maven.apache.org/POM/4. ...
- (转)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 + Jpa + Thymeleaf 增删改查示例
这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例. 先和大家聊聊我为什么喜欢写这种脚手架的项目,在我学习一门新技术的时候,总是想快速的搭建起一个 Demo 来试试它的效果,越 ...
随机推荐
- 表单中submit和button按钮的区别!
对于表单的按钮以前知道submit和button有区别,但没有深入探索,今天刚好又碰到这个问题,看了下网络上这位朋友已经有现成的总结了,而且比较到位,拿来跟大家分享下(原文地址:http://blog ...
- pandas 数据类型研究(一)数据转换
当利用pandas进行数据处理的时候,经常会遇到数据类型的问题,当拿到数据的时候,首先需要确定拿到的是正确类型的数据,一般通过数据类型的转化,这篇文章就介绍pandas里面的数据类型(data typ ...
- Java的浅克隆与深克隆
前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个对象,并对相应的属性进行数据,这样也能实现克隆的目的. 但当对象属性 ...
- MySql存储过程参考
Mysql(9)---纪录一次实际开发过程中用到的复杂存储过程 为了尽可能的还原当时为什么需要用到存储过程,下面我写了个详细的文档,我们可以从需求文档出发来分析. 有关存储过程之前也写了两篇文章来做铺 ...
- IDEA控制台中文乱码解决
关于IDEA中文乱码的解决方法,如下. 1.打开idea安装目录,选择 打开文件,末尾添加-Dfile.encoding=UTF-8 2.打开IntelliJ IDEA>File>Sett ...
- 006——转载-MATLAB数字与字符之间的转换
(一)参考文献:https://jingyan.baidu.com/article/5bbb5a1bd8dcb113eba1799d.html (二)数字转换成字符串 第一步在我们的电脑上打开matl ...
- 遍历器Iterator--指针对象
一. 什么是遍历器 1. 遍历器对象(Iterator) 遍历器对象本质上是一个指针对象,该对象有一个next方法,调用next方法返回一个 含有value和done属性的对象{value: val/ ...
- 项目 java.lang.NoClassDefFoundError 异常。
项目部署之后调用接口失败:异常信息: NoClassDefFoundError ClassNotFoundException 注意这两种是有区别的. 具体转 https://www.cnblogs.c ...
- slax自启动程序
Fluxbox 本身提供了自启动程序的功能.~/.fluxbox/startup 文件是一个像启动 Fluxbox 一样自启动应用程序的脚本.# 标记是注释. 一个简单的例子: #!/bin/sh # ...
- linux搭建代理服务器+蚁剑配置客户端代理
一:linux搭建代理服务器 0x00 介绍 关于搭建代理服务器的方法,我也是刚刚接触,从网上找了一些能够行得通的方法来给大家做个分享: 这里我用的是Tinyproxy作为代理服务软件.这个东西很小, ...