SpringAOP常见注解和执⾏顺序
⼤家好,我是DD。我⼀直强调基础很重要!⽽现在我们通常直接学Spring Boot和Spring Cloud之后,有不少⼩伙伴对于Spring AOP的了解就不那么充分了!所以,今天转了⼀篇不错的博⽂,来给⼤家巩固下Spring AOP的知识。
Spring ⼀开始最强⼤的就是 IOC / AOP 两⼤核⼼功能,我们今天⼀起来学习⼀下 Spring AOP 常见注解和执⾏顺序。
Spring Aop 的常⽤注解
⾸先我们⼀起来回顾⼀下 Spring Aop 中常⽤的⼏个注解:
@Before 前置通知:⽬标⽅法之前执⾏
@After 后置通知:⽬标⽅法之后执⾏(始终执⾏)
@AfterReturning 返回之后通知:执⾏⽅法结束之前执⾏(异常不执⾏)
@AfterThrowing 异常通知:出现异常后执⾏
@Around 环绕通知:环绕⽬标⽅法执⾏
常见问题
1、你肯定知道 Spring , 那说说 Aop 的去全部通知顺序, Spring Boot 或者 Spring Boot 2 对 aop 的执⾏顺序影响?
2、说说你在 AOP 中遇到的那些坑?
⽰例代码
下⾯我们先快速构建⼀个 spring aop 的 demo 程序来⼀起讨论 spring aop 中的⼀些细节。
配置⽂件
为了⽅便我直接使⽤ spring-boot 进⾏快速的项⽬搭建,⼤家可以使⽤ idea 的spring-boot 项⽬快速创建功能,或者去 start.spring.io 上⾯去快速创建spring-boot 应⽤。(因为本⼈经常⼿动去⽹上贴⼀些依赖导致,依赖冲突服务启动失败等⼀些问题)。
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group 'io.zhengsh'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven { url 'repo.spring.io/milestone' }
maven { url 'repo.spring.io/snapshot' }
}
dependencies {
# 其实这⾥也可以不增加 web 配置,为了试验简单,⼤家请忽略
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-aop'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
复制代码
接⼝类
⾸先我们需要定义⼀个接⼝。我们这⾥可以再来回顾⼀下 JDK 的默认代理实现的选择:
如果⽬标对象实现了接⼝,则默认采⽤JDK动态代理 如果⽬标对象没有实现接⼝,则采⽤进⾏动态代理 如果⽬标对象实现了接⼝,且强制Cglib,则使⽤cglib代理
这块的逻辑在 DefaultAopProxyFactory ⼤家有兴趣可以去看看。
public interface CalcService {
public int div(int x, int y);
}
实现类
@Service
public class CalcServiceImpl implements CalcService {
@Override
public int div(int x, int y) {
int result = x / y;
System.out.println("====> CalcServiceImpl 被调⽤了,我们的计算结果是:" + result);
return result;
}
}
aop
申明⼀个我们要为当前对象增加 @Aspect 和 @Component ,笔者之前也是才踩过这样的坑,只加了⼀个。
其实这块我刚开始也不是很理解,但是我看了 Aspect注解的定义我就清楚了
这⾥⾯根本就没有 Bean 的定义。所以我们还是乖乖的加上两个注解。还有就是如果当测试的时候需要开启Aop 的⽀持为配置类上增加@EnableAspectJAutoProxy 注解。
其实 Aop 使⽤就三个步骤:1、定义 Aspect 定义切⾯ 2、定义 Pointcut 就是定义我们切⼊点 3、定义具体的通知,⽐如: @After, @Before 等。
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* io.zhengsh.spring.service.impl..*.*(..))")
public void divPointCut() {
}
@Before("divPointCut()")
public void beforeNotify() {
System.out.println("----===>> @Before 我是前置通知");
}
@After("divPointCut")
public void afterNotify() {
System.out.println("----===>> @After 我是后置通知");
}
@AfterReturning("divPointCut")
public void afterReturningNotify() {
System.out.println("----===>> @AfterReturning 我是前置通知");
}
@AfterThrowing("divPointCut")
public void afterThrowingNotify() {
System.out.println("----===>> @AfterThrowing 我是异常通知");
}
@Around("divPointCut")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object retVal;
System.out.println("----===>> @Around 环绕通知之前 AAA");
retVal = proceedingJoinPoint.proceed();
System.out.println("----===>> @Around 环绕通知之后 BBB");
return retVal;
}
}
测试类
其实我这个测试类,虽然⽤了 @Test 注解,但是我这个类更加像⼀个 main ⽅法把:如下所⽰:
执⾏结论
结果记录:spring 4.x, spring-boot 1.5.9
⽆法现在依赖,所以⽆法试验
我直接说⼀下结论:Spring 4 中环绕通知是在最⾥⾯执⾏的
结果记录:spring 版本5.3.15 springboot 版本2.6.3
image.png
多切⾯的情况
多个切⾯的情况下,可以通过@Order指定先后顺序,数字越⼩,优先级越⾼。如下图所⽰:
代理失效场景
下⾯⼀种场景会导致 aop 代理失效,因为我们在执⾏ a ⽅法的时候其实本质是执⾏ AServer#a 的⽅法(MethodInterceptor)链,当我们在 a ⽅法内直接执⾏b(), 其实本质就相当于 this.b() , 这个时候由执⾏ a⽅法是调⽤到 a 的原始对象相当于是 this 调⽤,那么会导致b() ⽅法的代理失效。这个问题也是我们开发者在开发过程中最常遇到的⼀个问题。
@Service
public class AService {
public void a() {
System.out.println("...... a");
b();
}
public void b() {
System.out.println("...... b");
}
spring ioc注解}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论