在上一篇aop源码分析时,我们已经分析了一个bean被代理的详细过程,参考:https://www.cnblogs.com/yangxiaohui227/p/13266014.html

本次主要是分析目标方法的执行过程: 测试代码在git : https://gitee.com/yangxioahui/aopdemo.git

主要代码如下:

public interface Calc {
Integer add(int num1,int num2);
}
@Component
public class MyMath implements Calc { public Integer add(int num1,int num2){
return num1+num2;
} }
@Aspect
@Component
public class MyAspectJ {
@Pointcut(" execution(* com.yang.xiao.hui.aop.MyMath.*(..))")
private void pointcut(){}
@Before(value = "pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("before。。。。。");
}
@After(value = "pointcut()")
public void after(JoinPoint joinPoint){
System.out.println("after。。。。");
} @AfterReturning(value = "pointcut()")
public void afterReturning(){
System.out.println("afterReturning。。。");
} @AfterThrowing(value = "pointcut()")
public void afterThrowing(JoinPoint joinPoint){
System.out.println("afterThrowing。。。。");
} @Around(value ="pointcut()")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("around-before。。。。");
try {
Object proceed = joinPoint.proceed();
System.out.println("around-after_return。。。");
} catch (Throwable throwable) {
System.out.println("around-throw。。。"+throwable.getMessage());
}
System.out.println("around-after。。。");
}
}
@Configuration
@ComponentScan("com.yang.xiao.hui.aop")
@EnableAspectJAutoProxy()
public class App
{
public static void main( String[] args )
{ ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);
Calc myMath = (Calc)ctx.getBean("myMath");
myMath.add(3,5);
System.out.println(ctx.getBean("myMath").getClass()); }
}

测试: MyMath  类的add 方法执行过程,debug 调试:

上一篇源码分析,我们已经分析过,最终会被JdkDynamicAopProxy 的invoke方法调用,下面分析下该方法:

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; //里面封装了被代理对象,也就是MyMath
Object target = null; //被代理对象 try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { //处理equals方法
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { //处理Hashcode 方法
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {//显然,我们的方法定义所在的类是Calc
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
         //this.advised.opaque 这个值是说代理类能否强转为Advised,flase是可以强转,在代理对象创建源码时分析过了,代理对象确实实现了Advised接口
//method.getDeclaringClass().isInterface() 我们的目标方法定义所在类是Calc,确实是一个接口,但该接口没有实现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); //这里是直接反射调用方法: method.invoke(target, args);
} Object retVal; if (this.advised.exposeProxy) { //@EnableAspectJAutoProxy 注解的一个属性,为true时,就会将代理对象
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);//将当前的代理对象存到ThreadLocal中
setProxyContext = true;
} // Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget(); //获取被代理类的实例,在这里是MyMath的实例
Class<?> targetClass = (target != null ? target.getClass() : null); //在这里是com.yang.xiao.hui.aop.MyMath // Get the interception chain for this method. 将advisors封装成MethodInterceptor,在代理对象源码的博客介绍过,@before等注解标注的方法会被封装成advisor,所以这里取出来,封装一下,下面会详细分析该方法
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); ////这里是直接反射调用方法: method.invoke(target, args);
}
else {
// We need to create a method invocation...
MethodInvocation 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())) { //特殊情况,责任链调用的返回值就是被代理对象(target),而方法的返回类型要求的是代理对象Proxy,这样就将代理对象返回去就可以了
// 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()) { //如果方法的返回类型不是void,并且是基本类型,那么,如果执行调用后,拿到的返回值是空,就抛异常
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;//返回得到的结果
}
finally {
if (target != null && !targetSource.isStatic()) { //如果目标代理对象不为空,同时每次调用targetSource.getTarget()返回的对象不同,也就是非单例的情况下需要释放目标对象
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy); //方法调用完毕,将ThreadLocal中的代理对象设置回之前的那个
}
}
}

//通过上面的分析,目标方法在执行时,主要分两步,取得拦截链,调用拦截链:

先分析: List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 其中AdvisedSupport: advised拥有被代理对象和advisors,在代理对象创建源码分析时,有分析过

下面分析获取chain的过程:

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) { // This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
Advisor[] advisors = config.getAdvisors(); // config 就是AdvisedSupport,它拥有advisors,在代理对象创建过程中分析过
List<Object> interceptorList = new ArrayList<>(advisors.length); //要将advisor转成方法拦截器,才能起到拦截的作用
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());//被代理类
Boolean hasIntroductions = null; for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) { //根据advisor的类型不一样,转成MethodInterceptor 逻辑也不一样
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
           //config.isPreFiltered()都是返回true,代表advisors是否已经是过滤过的了
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {//主要通过切入点表达式再次校验目标类是否需要被切入
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();// 如果根据切入点表达式判断了类需要被切入,那之后就要判断方法是否需要切入,因为一个类有很多方法,并不是所有方法都需要被切入
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) { //根据方法匹配器的类型,使用不同发匹配方式
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
match = mm.matches(method, actualClass);
}
if (match) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor); //如果advisor 匹配上了,那就要封装成方法拦截器
if (mm.isRuntime()) { /mm的类型主要有2种,一种是动态方法匹配器,永远返回true,一种是静态方法匹配器,永远返回false
// 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)); //将所有符合要求的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;
}

上面的方法主要是根据advisor的类型,使用不同的方法匹配器,看看目标的方法是否可以使用该advisor,是的话,就封装成MethodInterceptor,看看封装的逻辑:registry.getInterceptors(advisor);

@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
for (AdvisorAdapter adapter : this.adapters) { //三种适配器: MethodBeforeAdviceAdapter/ AfterReturningAdviceAdapter/ThrowsAdviceAdapter 看看哪个匹配上,就用哪一种适配器来封装成拦截器
if (adapter.supportsAdvice(advice))
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[0]);
}

//advisor封装成interceptor的一个例子:

拦截器链获取分析完毕了,那么,我们接下来分析下,责任链的执行:

之后我们看看:invocation.proceed();

public Object proceed() throws Throwable {
//刚开始currentInterceptorIndex=-1
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //如果最后一个拦截器执行完毕后,就直接反射调用目标方法
return invokeJoinpoint(); //method.invoke(target, args);
} 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.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, 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.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); //如果是静态的匹配的,就直接调用目标方法
}
}

debug调试调用过程:

先看看我们的链条都有哪些:

总共有六个拦截器:

最后总结:

springboot对aop的自动装配机制:我们在springboot项目中,没有自己给启动类贴上@EnableAspectJAutoProxy 注解,aop一样生效,原理是有个配置类:

默认值如下:

我们可以在yml中配置相应的属性:

spring aop 源码分析(二) 代理方法的执行过程分析的更多相关文章

  1. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  4. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  5. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  6. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  7. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  8. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  9. spring aop 源码分析(三) @Scope注解创建代理对象

    一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ...

随机推荐

  1. 用 Python 写个七夕表白神器

    今天是七夕节,相比于现代人自创的 502,不对是 520,七夕才是中国传统意义上的情人节,本文分享几个 Python 表白程序,情侣可以现学现用,单身的话也可以先收藏一下,说不定下次就用上了. 爱心树 ...

  2. 喵的Unity游戏开发之路 - 在球体上行走

    很多童鞋没有系统的Unity3D游戏开发基础,也不知道从何开始学.为此我们精选了一套国外优秀的Unity3D游戏开发教程,翻译整理后放送给大家,教您从零开始一步一步掌握Unity3D游戏开发. 本文不 ...

  3. springboot+themeleaf+bootstrap访问静态资源/无法访问静态资源/图片

    在网页HTML上访问静态资源的正确写法例: 1.<img src="../../static/bootstarp/img/2.jpg"     th:src="@{ ...

  4. Go语言从入门到高薪之路(一)-- 初识与安装

    为什么要学习Go语言? Go语言又称Golang,它是21世纪的编程语言,学好了就能拿高薪,拿了高薪就能实力装逼,有了实力就能泡妹子...(我就问你吊不吊,想不想学?) Go语言有什么特点和优势? G ...

  5. ASP.NET Core3.1使用IdentityServer4中间件系列随笔(三):创建使用[ClientCredentials客户端凭证]授权模式的客户端

    配套源码:https://gitee.com/jardeng/IdentitySolution 上一篇<ASP.NET Core3.1使用IdentityServer4中间件系列随笔(二):创建 ...

  6. 免费领CRMEB移动社交电商系统源码与授权

    移动电商风起云涌,直播带货重塑销售模式,传统商业更是举步维艰,各行各业转型移动电商迫在眉睫,拥有一款好的移动社群社交电商系统成为众多企业与商家的心病! 你曾是否被那些劣质的移动电商系统搞得心力憔悴? ...

  7. SplashImage

    关于Unity启动时间过长(启动黑屏时间长)的问题 https://blog.csdn.net/h5q8n2e7/article/details/50484458 Unity3D游戏开发之“重写Uni ...

  8. php中无参函数的RCE

    学习一下php中无符号的问题. 1.无参数 <?php if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { ev ...

  9. 超详细,Windows系统搭建Flink官方练习环境

    如何快速的投入到Flink的学习当中,很多人在搭建环境过程中浪费了太多的时间.一套一劳永逸的本机Flink开发环境可以让我们快速的投入到Flink的学习中去,将精力用在Flink的原理,实战.这也对于 ...

  10. 用Maven给一个Maven工程打包,使用阿里云镜像解决mvn clean package出错的问题,使用plugin解决没有主清单属性的问题

    本来在STS里做了一个极简Maven工程,内中只有一个Main方法的Java类,然后用新装的Maven3.6.3给它打包. 结果,Maven罢工,输出如下: C:\personal\programs\ ...