spring AOP源码分析(三)
在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib这两种方式下,拦截器调用的实现。
一 JDK动态代理拦截器调用的实现:
我们知道,在生成代理对象时,相关的拦截器已经配置到代理对象中了。Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 这行代码是用于生成代理对象的,this代表InvocationHandler接口,此处是指JdkDynamicAopProxy类,
因为JdkDynamicAopProxy实现了InvocationHandler接口。当生成的代理对象调用代理方法时,会触发InvocationHandler接口中invoke方法的调用,而增强行为的发生或者说对目标对象方法的拦截动作就是在这个方法中。我们进入JdkDynamicAopProxy
类的invoke方法:
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
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 {
// 如果目标对象没有实现Object的equals方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// 如果目标对象没有实现Object的hashCode方法
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else 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.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 否则,需要先调用拦截器然后再调用目标方法,通过构造一个ReflectiveMethodInvocation来实现
// 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 != Object.class && 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);
}
}
}
当没有拦截器时,我们进入invokeJoinpointUsingReflection这个方法,可以看到通过反射的方式直接调用目标对象方法
/**
* Invoke the given target via reflection, as part of an AOP method invocation.
* @param target the target object
* @param method the method to invoke
* @param args the arguments for the method
* @return the invocation result, if any
* @throws Throwable if thrown by the target method
* @throws org.springframework.aop.AopInvocationException in case of a reflection error
*/
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
throws Throwable { // Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
// 通过反射的方式直接调用目标对象方法
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
当有拦截器时,我们进入invocation.proceed()方法,这个方法就是AOP的核心部分了,AOP是如何完成对目标对象的增强的?这些实现封装在AOP拦截器链中,由一个个具体的拦截器实现的,就在这个方法中。
@Override
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 从索引为-1的拦截器开始调用,按顺序递增,直到没有拦截器了,然后开始调用目标对象的方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 沿着定义好的拦截器链进行获取然后逐个处理
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
// 这里是触发匹配判断的地方,如果和定义的pointcut匹配,那么这个advice将得到执行
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// 如果是一个interceptor,那么直接调用这个interceptor的invoke方法 进入此方法来分析通知(拦截器)是如何起作用的
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
进入MethodBeforeAdviceInterceptor类的invoke方法,就是前置通知调用的方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); // 此处就是发生的切面增强行为
return mi.proceed(); //递归调用proceed方法,然后进入下一个拦截器的处理
}
进入AspectJAfterAdvice类的invoke方法,就是后置通知调用的方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed(); //递归调用proceed方法
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null); //此处就是发生切面增强行为的地方
}
}
接下来我们考虑另外一个问题:在xml中定义的通知是如何配置到代理对象中的拦截器链中的?
我们知道,我们是通过 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);这行代码获取拦截器的,所以所有的拦截器都在interceptorsAndDynamicMethodMatchers这个list集合中,那么
这个集合从哪个地方获取的呢?我们可以进入JdkDynamicAopProxy类的invoke方法,有这么一行代码:List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);用于获取这个代理方法的拦截器链,进入此方法:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
// 这里使用cache获取已有的interceptor链,如果是第一次,需要手动生成,这里是使用AdvisorChainFactory来生成interceptor链的,默认是DefaultAdvisorChainFactory
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
DefaultAdvisorChainFactory 由名字可知,这是一个生成通知器链的工厂。进入getInterceptorsAndDynamicInterceptionAdvice这个方法:
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
// 所有的advistor在config中已经持有了,可以直接使用
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
//通过AdvisorAdapterRegistry注册器,把从config中获取的advistor进行适配,从而获得拦截器,再把它放入List中。这就是拦截器注册的过程。
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} return interceptorList;
}
以上就是JDK动态代理springAOP实现增强行为的过程,对于cglib这种方式,此处就不做分析。
spring AOP源码分析(三)的更多相关文章
- 5.2 spring5源码--spring AOP源码分析三---切面源码分析
一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- Spring AOP 源码分析 - 筛选合适的通知器
1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...
- Spring AOP 源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
- spring aop 源码分析(三) @Scope注解创建代理对象
一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ...
随机推荐
- jsp 或 php 等view之中使用javascript简单处理的使用技巧
前端人员在jsp,php等后台mvc之的coding之时,前端人员常常需要一些少量的数据处理,直接使用js的方法无疑是开销最小的.使用的方法 使用在标签之中嵌套script标签,使用document. ...
- Scrapy 框架 CrawlSpider 全站数据爬取
CrawlSpider 全站数据爬取 创建 crawlSpider 爬虫文件 scrapy genspider -t crawl chouti www.xxx.com import scrapy fr ...
- Gps定位和wifi定位和基站定位的比较
现在手机定位的方式是:Gps定位,wifi定位,基站定位 Gps定位的前提,手机开启Gps定位模块,在室外,定位的精度一般是几米的范围 wifi定位的前提,手机要开启wifi,连不连上wifi热点都可 ...
- iptables 从一台机到另一台机端口转发
启用网卡转发功能#echo 1 > /proc/sys/net/ipv4/ip_forward 举例:从192.168.0.132:21521(新端口)访问192.168.0.211:1521端 ...
- Python:Day24 类、类的三大特性
Python有两种编程方式:函数式+面向对象 函数式编程可以做所有的事情,但是否合适? 面向对象: 一.定义 函数: def + 函数名(参数) 面象对象: class bar---> 名字 ...
- hyperledge环境安装
1.安装环境 1)本机安装 前提是已经安装好了docker\docker-compose\go,相应的内容可见 docker-1-环境安装及例子实践 docker官方文档学习-1-Docker for ...
- leetcode:Single Number
public int SingleNumber(int[] nums) { if(nums==null||nums.Length%2==0) return 0; int ret=nums[0]; fo ...
- Oracle 取前几条记录
今天看了篇文章,对oracle取前几条数据的方式和说明,总结比较全,学习了,做个记录点.oracle 取前10条记录 以下内容是原始文章内容,用于做留存阅读. 1.oracle 取前10条记录 1) ...
- maven下载及安装
一.下载及安装 1.1 下载maven 3.1.1 先到官网http://maven.apache.org/download.cgi 下载最新版本(目前是3.1.1 ),下载完成后,解压到某个目录,本 ...
- Java技术——Java中的static关键字解析
)非静态内部类能够访问外部类的静态和非静态成员,显然一个非静态内部类不能脱离外部类实体被创建,而静态类不能访问外部类的非静态成员,它只能访问外部类的静态成员.这一点和上面static方法的性质类似. ...