注解版本-分析SpringAOP底层原理

本文详细介绍了Spring AOP的原理,包括AOP的概念、相关注解如@Aspect、@Before等,以及底层的代理机制。通过@EnableAspectJAutoProxy注解启动AOP,AspectJAutoProxyRegistrar注册AnnotationAwareAspectJAutoProxyCreator,后者作为BeanPostProcessor在对象创建前后进行增强。文章通过代码解析了代理对象如何调用方法,展示了切面方法的执行顺序。最后,提供了一个简单的测试用例来验证AOP的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1)AOP含义

2)相关注解

3)底层解析

1、@EnableAspectJAutoProxy

 2、AspectJAutoProxyRegistrar

 3、AnnotationAwareAspectJAutoProxyCreator

a、postProcessBeforeInstantiation

 b、postProcessAfterInitialization

 4、代理对象调用方法

4)相关测试类


1)AOP含义

面向切面编程,也就是动态代理,在程序运行期间,动态将增强(通知)方法切到运行方法中,在不改变原来代码的基础上对代码进行动态增强。

2)相关注解

@EnableAspectJAutoProxy:开启基于注解的AOP代理

@Aspect:告诉spring容器标识该注解的类是切面类,里面包含了通知方法,后续spring容器会去扫描该类的通知方法。注意:切面类需要注册到spring容器中。

@Before:前置通知

@After:后置通知

@Around:环绕通知,搭配ProceedingJoinPoint使用,通过ProceedingJoinPoint的proceed()方法运行切入点方法

@AfterReturning:返回通知

@AfterThrowing:异常通知

3)底层解析

注意点:解析@EnableXX,通常看向容器注册了什么组件,组件的运行时机,组件的具体作用

1、@EnableAspectJAutoProxy

@EnableAspectJAutoProxy利用@Import注解向容器导入AspectJAutoProxyRegistrar组件

 2、AspectJAutoProxyRegistrar

AspectJAutoProxyRegistrarImportBeanDefinitionRegistrar接口的实现类,容器创建对象时,会调用registerBeanDefinitions方法,利用registerBeanDefinitions方法向容器注册名为

org.springframework.aop.config.internalAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator对象

 3、AnnotationAwareAspectJAutoProxyCreator

 通过阅读AnnotationAwareAspectJAutoProxyCreator和他的父类源码可发现该类是一个BeanPostProcessor后置处理器,实现了SmartInstantiationAwareBeanPostProcessorBeanFactoryAware接口

 根据SmartInstantiationAwareBeanPostProcessor接口的特性,容器在创建对象时会依次调用对应方法,如图

 我们按照顺序,依次进入方法

a、postProcessBeforeInstantiation

该方法是在对象创建之前调用的,

 isInfrastructureClassshouldSkip方法可提前过滤掉不需要增强的类,例如标识了@Aspect注解的切面类,Spring事务中的事务增强器组件等,将结果缓存到advisedBeans集合中。

 若是有自定义的目标对象,根据getAdvicesAndAdvisorsForBean方法获取对应的增强器,若是返回增强器集合不为空,则说明该对象需要被增强,通过createProxy方法创建代理对象并返回给容器,代理对象包含了目标对象、增强器等信息。

进入getAdvicesAndAdvisorsForBean 方法,这个方法是根据对象类名查询容器中是否含有符合资格的增强器,若是有,则返回增强器Advisor集合

 进入findEligibleAdvisors方法,findCandidateAdvisors方法是得到容器中所有可用的候选增强器(例如Spring事务配置的增强器、@Aspect切面类下的通知方法),findAdvisorsThatCanApply方法是判断候选增强器哪些可应用于组件,若是有可应用的增强器,则将增强器进行排序,并添加容器额外的增强器

进入createProxy方法底层,可得到如下图,创建代理对象

如图3个方法都是默认返回,不做解释

 b、postProcessAfterInitialization

 该方法是容器创建对象并初始化完成后调用的,通过wrapIfNecessary方法判断如果对象含有对应的增强器则将对象包装成代理对象并返回给容器

 进入wrapIfNecessary方法,跟之前postProcessBeforeInstantiation方法有点类似,先是根据advisedBeans集合判断,再根据isInfrastructureClass、shouldSkip方法重复判断一次,这里重复判断一次,我还未了解是为什么,等后期如果知道了,在解释原因

在此根据getAdvicesAndAdvisorsForBean方法获取对应的增强器,通过createProxy方法创建代理对象并返回给容器,代理对象包含了目标对象、增强器等信息

 4、代理对象调用方法

从容器获取到的代理对象调用方法,以Cglib代理对象为例

调用方法进入CglibAopProxy的intercept方法

 通过getInterceptorsAndDynamicInterceptionAdvice方法找到的调用方法的增强器集合并转换成

Interceptor类型的拦截器链

  若是返回的chain集合为空,则直接运行目标方法

 若是得到的拦截器链chain不为空,则创建CglibMethodInvocation对象并运行proceed方法

进入proceed方法,currentInterceptorIndex是从-1开始计数,

若interceptorsAndDynamicMethodMatchers拦截器链总数为0,则相等,直接运行目标方法;

若interceptorsAndDynamicMethodMatchers拦截器链总数不为0,

当currentInterceptorIndex值等于拦截器链总数-1,则说明拦截器已经运行完,可直接运行目标方法。

 每一次调用CglibMethodInvocation的proceed方法都会将currentInterceptorIndex加1,

根据currentInterceptorIndex从拦截器链中获取拦截器,

第一个是 ExposeInvocationIntercept拦截器

ExposeInvocationIntercept是SpringAop内置的增强拦截器,往下进行,运行ExposeInvocationInterceptinvoke方法,将当前CglibMethodInvocation对象传进去

 将CglibMethodInvocation对象保存到ThreadLocal中,并再次运行CglibMethodInvocationproceed方法,

 再次回到proceed方法,由于currentInterceptorIndex已经加1,在接下来获取就是第二个拦截器AspectAfterThrowingAdvice,然后再依次运行调用拦截器AspectAfterThrowingAdviceinvoke方法,

 又一次递归调用CglibMethodInvocationproceed方法,直至运行出现异常或者currentInterceptorIndex值等于拦截器链总数-1,运行目标方法,依次根据调用顺序返回

示意图大概如下

 其中AspectAroundAdvice处理有些不同,进入invoke方法

ProceedingJoinPoint对象传给invokeAdviceMethod方法,利用方法的反射方法,运行标识@Around的方法,并将ProceedingJoinPoint对象传进来

然后根据用户自定义决定是否运行proceedingJoinPoint.proceed(),

proceedingJoinPoint实际类型为MethodInvocationProceedingJoinPoint

查看proceed源码可知该方法再一次递归调用了CglibMethodInvocationproceed方法,继续遍历拦截器链集合运行invoke方法

运行结果如图

 若是没运行proceedingJoinPoint.proceed(),则不会运行目标方法,后续的拦截器也不会运行

 运行结果如图

4)相关测试类

目标类

package com.mzp.component.aop;

import org.springframework.stereotype.Component;

@Component
public class MathCalculator {
    public MathCalculator() {
        System.out.println("MathCalculator");
    }

    public int div(int i, int j){
        System.out.println("mathcalculator div方法运行");
        return i/j;
    }
}

切面类

package com.mzp.component.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LogAspect {
    public LogAspect() {
        System.out.println("LogAspect");
    }

    @Pointcut("execution(public int com.mzp.component.aop.MathCalculator.div(..))")
    public void pointcut(){

    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        System.out.println("before 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()));
    }

    @After("pointcut()")
    public void after(JoinPoint joinPoint){
        System.out.println("after 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()));
    }

    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result){
        System.out.println("afterReturning 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()) + " 返回值" + result);
    }

    @AfterThrowing(value = "pointcut()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception){
        System.out.println("afterThrowing 方法" + joinPoint.getSignature().getName() + " 参数" + Arrays.asList(joinPoint.getArgs()) + " 异常" + exception);
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around 前");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("around 后");
        return proceed;
    }
}

配置类

package com.mzp.component.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.mzp.component.aop")
@EnableAspectJAutoProxy
public class AopConfig {
}

测试类

@Test
    public void test10(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
        mathCalculator.div(1,2   );
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值