底层实现:通过动态代理实现
AOP核心概念
相关注解
类注解:
@Component 表示将该类交给IOC容器管理
@Aspect 表示这是个切面类
方法注解:
通过切入点表达式指定面向哪些连接点编程,即该方法的作用范围
例:@Around(“execution(*tlias.service.impl.DeptServiceImpl.*(..))”)
注:切入点表达式具体写法再后面会讲到
AOP执行流程:
原理:代码执行中需要注入这个对象,而这个对象中的方法是被AOP标记了的。所以在注入对象时,会根据原始对象生成一个代理对象(代理对象中的方法就是通过AOP增强后的方法)。再调用注入的对象的方法时,执行的就是代理对象的方法,也就执行了我们AOP需要增强的代码逻辑。所以AOP增强代码逻辑实现的前提是,代码运行时需要注入一个该对象,并且在后续调用了该对象被AOP增强的那个方法。
代码实际执行流程:在执行该方法时,由于调用该方法的对象是注入的是代理对象,而代理对象的方法是经过AOP增强的方法,所以代理对象的方法中中包含我们再方法运行前要执行的代码逻辑,原本的方法逻辑,以及我们再方法运行后要执行的代码逻辑,按照这个顺序依次执行。
解决我之前存在的一些困惑:
实际执行中,应该就是直接调用这个代理对象的方法,代理对象的方法包含了所有的代码逻辑。
但是我们在断点测试时,由于看不见这个代理对象(因为创建对象就需要对应的实体类,但是我们本身没有创建这个实体类,而是通过动态代理创建的对象)。所以我们测试时,代码只能通过在AOP方法和原始方法间来回跳转来执行代码:先跳转到AOP方法执行运行前逻辑,再跳转到对象类执行方法,再跳回AOP方法执行运行后逻辑。
但这并不表示实际上执行的是AOP中的方法,和原始对象类中的方法,实际上执行的则是代理对象中增强后的方法。
通知类型
@before @After @AfterReturning @AfterThrowing
方法返回值均为void,加上注解以后,直接在方法体中书写要执行的逻辑即可。这些逻辑在实际运行时就会在方法前或后执行,具体的执行顺序参照上图即可。
@Around
参数列表为什么有一个ProceedingJoinPoint : 由于@Around通知,既包含了方法执行前通知,方法执行后通知,所以得对前后进行区分,所以该方法的参数列表里有一个ProceedingJoinPoint对象,通过这个对象我们可以调用原始方法执行,以此来区分该方法执行前后需要执行的代码逻辑。
返回对象为什么是Object :由于调用的原始方法可能是存在返回值的,我们也需要把原始方法的返回值传递出去,不影响代码的正常运行。
存在的疑问:
为什么除去@Around需要我们手动返回原始对象的返回值,而其他通知类型不需要返回原始方法的返回值呢,那些原始方法的返回值也可能不为null呀。
猜测:
可能是因为除@Around外的通知类型,代码执行流程简单(只需要在代码运行前或者代码运行后执行即可),所以底层已经自动帮我们返回了原始方法的返回值,所以不需要我们手动返回原始方法的返回值了。
查找资料得出的结果:
为什么 @Around
需要手动返回原始方法的返回值
-
控制权:
@Around
通知提供了最大的灵活性,因为它允许你完全控制目标方法的调用过程。- 在
@Around
通知中,你可以决定何时调用目标方法、如何调用以及如何处理返回值。 - 这种灵活性要求你在通知中显式地调用
proceedingJoinPoint.proceed()
方法来执行目标方法,并且需要显式地处理和返回目标方法的返回值。
-
动态行为:
@Around
通知可以用来实现动态代理,例如,可以有条件地调用目标方法或返回不同的结果。- 这种动态行为需要开发者显式地控制方法的执行流程。
@Before
、@After
、@AfterReturning
、 @AfterThrowin
g这些
通知类型,它们都是通过动态代理机制实现的。这些通知类型的设计目的是在目标方法调用前后执行某些逻辑,而不干扰目标方法本身的执行流程。因此,Spring AOP 代理对象会自动处理目标方法的调用,并将结果返回给调用方。Spring AOP 代理对象会自动处理目标方法的调用和返回值,因此这些通知类型不需要显式返回目标方法的返回值。而 @Around
通知则提供了更大的灵活性,需要显式地调用目标方法并返回结果。
通知的执行顺序
情景:当有多个切面的切入点匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。
切入点表达式
@execution切入点表达式
语法
通配符号
书写建议
常用标签:@Pointcut
使用场景:切入点表达式需要多次重复书写时,可以使用@Pointcut注解声明一个方法
使用方法:在需要用到该切入点表达式时,直接在通知类型的注解上调用该方法即可
使用试例:
注意: 1.切入点表达式的方法权限修饰符为private则只能在本类中访问该方法。
2.在需要输入切入点表达式的地方,IDEA会自动提示该方法。
@annotation切入点表达式
介绍:用于匹配标识有特定注解的方法
例:
使用方法:(也可以通过@pointcut注解来使用)
1.自定义一个注解
2.在@annotation注解中指定包名类名等,最后指定需要匹配的注解
3.在需要指定的方法上加该注解即可
按顺序依次举例如下
优势: 可以更灵活的匹配需要的方法
缺点:需要自己依次在对应方法上加自定义的注解
连接点
介绍
理解:子类型在继承父类方法的基础上还可以增加一些特有方法,所以@Around通知要比其他通知类型获取的相关信息多。
接入点信息获取 试例代码:
@around
其他通知类型
各通知类型可获取的接入点信息
1. @Before
通知
作用:在目标方法调用之前执行。
连接点信息:
- 签名(Signature):获取目标方法的签名信息,包括方法名、参数类型等。
- 参数(Arguments):获取目标方法的传入参数。
2.@After
通知
作用:在目标方法执行之后执行,无论方法是否抛出异常。
连接点信息:
- 签名(Signature):获取目标方法的签名信息。
- 参数(Arguments):获取目标方法的传入参数。
- 目标对象(Target):获取被代理的目标对象。
- 代理对象(This):获取当前执行通知的代理对象。
3. @AfterReturning
通知
作用:在目标方法成功返回后执行。
连接点信息:
- 签名(Signature):获取目标方法的签名信息。
- 参数(Arguments):获取目标方法的传入参数。
- 目标对象(Target):获取被代理的目标对象。
- 代理对象(This):获取当前执行通知的代理对象。
- 返回值(Return Value):获取目标方法的返回值。
4. @AfterThrowing
通知
作用:在目标方法抛出异常后执行。
连接点信息:
- 签名(Signature):获取目标方法的签名信息。
- 参数(Arguments):获取目标方法的传入参数。
- 目标对象(Target):获取被代理的目标对象。
- 代理对象(This):获取当前执行通知的代理对象。
- 异常对象(Thrown Exception):获取目标方法抛出的异常对象。
在 Spring AOP 中,@Around
通知是最强大的通知类型之一,因为它允许你完全控制目标方法的调用过程。@Around
通知提供了对连接点(JoinPoint)的全面访问,并且允许你在方法调用前后执行自定义逻辑。以下是 @Around
通知可以获取的连接点信息:
5.@Around
通知
-
签名(Signature):
JoinPoint
的getSignature()
方法可以获取目标方法的签名信息。- 这包括方法名、参数类型等。
-
方法参数(Arguments):
JoinPoint
的getArgs()
方法可以获取目标方法的传入参数。
-
目标对象(Target):
JoinPoint
的getTarget()
方法可以获取被代理的目标对象。- 这是原始的目标对象,即你实际想要代理的对象。
-
代理对象(This):
JoinPoint
的getThis()
方法可以获取当前执行通知的代理对象。- 这是经过 Spring AOP 代理后的对象。
-
返回值(Return Value):
- 在
@Around
通知中,你需要显式地调用ProceedingJoinPoint.proceed()
方法来执行目标方法,并且可以获取其返回值。 - 你可以选择是否继续执行目标方法,或者直接返回一个自定义的结果。
- 在
-
异常(Thrown Exception):
- 如果目标方法抛出了异常,你可以捕获该异常并进行处理。
使用场景
1. 日志记录
应用场景:在系统中记录方法的调用时间和执行结果,对于调试和运维非常重要。
2. 性能监控
应用场景:记录方法执行的时间,帮助优化性能瓶颈。
3. 安全性和认证
应用场景:确保只有授权的用户才能访问特定的方法或资源。
4. 事务管理
应用场景:确保业务逻辑中的方法在执行过程中遵循事务一致性原则。
5. 缓存
应用场景:缓存方法的结果,减少不必要的重复计算。
6. 异常处理
应用场景:统一处理方法中抛出的异常,并记录日志或进行其他操作。
7. 通知和事件发布
应用场景:在方法执行前后发送通知或发布事件。
总结
面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它允许将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,从而提高代码的模块化程度。在实际开发中,AOP 有许多典型的应用场景,可以简化开发工作并提高代码的可维护性,帮助你更好地组织和管理代码。通过使用 AOP,你可以将横切关注点从业务逻辑中分离出来,提高代码的可读性和可维护性。