在上一篇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. shell 三剑客之 sed

    sed 在shell 编程里也很常用,功能强大! 同grep一样,sed提供两种方式: 方式一:stdout | sed [option] "pattern command" 从文 ...

  2. SpringBoot2搭建基础架构——开源软件诞生4

    技术框架篇--第4篇 用日志记录“开源软件”的诞生 赤龙ERP开源地址: 点亮星标,感谢支持,加微信与开发者交流 kzca2000 码云:https://gitee.com/redragon/redr ...

  3. 03.AOF持久化机制配置与工作流程

    一.AOF持久化的配置 配置文件redis.conf,AOF持久化默认是关闭的,默认是打开RDB持久化 appendonly yes     二.工作流程: 打开AOF持久化机制之后,redis每次接 ...

  4. 攻防世界——web新手练习区解题记录<1>(1-4题)

    web新手练习区一至四题 第一题view_source: 题目说右键不管用了,我们先获取在线场景来看一看,我们看到这样一个网页,并且右键确实点了没什么反应,而用到右键一般就是查看网页源码 用快捷键(F ...

  5. mysql 8.0.11安装教程

    安装环境:win7 1. 下载安装包 下载地址:https://dev.mysql.com/downloads/file/?id=476233 2. 解压zip包 3. 初始化my.ini 创建my. ...

  6. P3311 [SDOI2014]数数 AC自动机+数位DP

    题意 给定一个正整数N和n个模式串,问不大于N的数字中有多少个不包含任意模式串,输出对\(1e^9+7\)取模后的答案. 解题思路 把所有模式串都加入AC自动机,然后跑数位DP就好了.需要注意的是,这 ...

  7. ASP导出数据到excel遇到的一些问题

    一直用动易平台的ASP做新闻发布网站,直到现在才接触导出数据到Excel的问题,目的在于公司要统计各部门的投稿量,要做这么个东西,实现起来是挺简单的,但是第一次做,还是费了一些功夫的,特此记录一下 主 ...

  8. 剑指 Offer 54. 二叉搜索树的第k大节点

    题目描述 给定一棵二叉搜索树,请找出其中第k大的节点. 示例1: 输入: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \ 2 输出: 4 示例2: 输入: root ...

  9. 用于测试 SqlAnalyzer1.01 的21个测试用例

    感慨:当年看着 https://www.cnblogs.com/heyang78/p/11451814.html 一文望洋兴叹,如今我也自己做出来了! 21. 原文=select name, cnt ...

  10. selenium+python对表格数据的操作

    一.直接获取整个表格数据,包含表头 def table_info(self): tr_data=[] table_data=[] css='id=>useradmin'#根据表格id找到表格 s ...