光标放在要添加异常的那行,ALT+ENTER自动添加异常处理
目录
🍁添加依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>3.5.10</version>
</dependency>
🍁简单上手AOP

1.@Aspect:标识着这是一个切面类,至于切面类是啥,先不讲,我先大概的介绍一下。
2.@Around:表示这个目标方法在前后都会被执行。记住,目标方法不是此注解修饰的方法。
3.pjp.proceed:执行目标方法。
4.pjp.getSignature:方法签名,如下图

5.throws Throwable异常处理:proceed方法要处理的异常,如下

🍁执行效果

所以有啥用呢,很明显,我们使用aop,能解耦,能在不改变原有方法的基础上,添加额外功能。而且还能减少我们写重复代码的成本,一定程度上提高了代码的可读性。
🍁AOP详解
🍀切点

前后两个红框框的就是切点。
我标红框框的后面那个就是切点表达式,
切点表达式:对哪个原方法进行代码增强。
切点:就是切点啊,名字是这么叫的哈哈。
🍀连接点

里面的com.example.bookslistdemo.controller路径下的所有类底下的所有方法都是连接点也就是要增强的方法嘛。
🍀通知

红框框的这部分代码逻辑就是通知。
🍀切面

包含切点和返回值的通知就是切面,也就是上图标红框框的就是切面。
🍁通知类型
一共5个通知
@Around:环绕通知,这个不用多讲。
@Before:前置通知,在目标方法前被执行。
@After:后置通知,在目标方法后被执行。
@AfterReturning:返回后通知,在目标方法后被执行,有异常不会执行!
@AfterThrowing:异常后通知,在目标方法在发生异常后执行!
我们用代码看一看他们的执行顺序,
java
@Slf4j
@Aspect
@Component
public class TimeAspect {
@Before("execution(* com.example.bookslistdemo.controller.*.*(..))")
public void doBefore(){
log.info("执行Before方法");
}
@After("execution(* com.example.bookslistdemo.controller.*.*(..))")
public void doAfter(){
log.info("执行After方法");
}
@AfterReturning("execution(* com.example.bookslistdemo.controller.*.*(..))")
public void doAfterReturning(){
log.info("执行AfterReturning方法");
}
@AfterThrowing("execution(* com.example.bookslistdemo.controller.*.*(..))")
public void doAfterThrowing(){
log.info("执行AfterThrowing方法");
}
@Around("execution(* com.example.bookslistdemo.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
log.info("Around 方法开始执行");
Object result=pjp.proceed();
log.info("Around 方法结束执行");
return result;
}
}
然后访问user/login方法,
执行结果发现,

然后我们发现其实,好像只有@Around注解有返回值Object和参数ProceedingJoinPoint pjp吧,其它的都是清一色的void返回值,并且没有参数!
🍁@PointCut
上面我们写的那种写法,还得每个注解后面加上那么一长串路径,看着就头疼,所以我们可以借助@PointCut注解来简化我们的代码
具体如下
java
@Pointcut("execution(* com.example.bookslistdemo.controller.*.*(..))")
private void pt(){}
@Before("pt()")
public void doBefore(){
log.info("执行Before方法");
}
@PointCut注解后跟切点表达式,来定义路径。
此后,如果需要用到这个路径,就在注解后面加上@PointCut注解修饰的方法名即可。
这样就极大的简化了我们的代码,也提高了代码可读性。
🍁切面优先级
🍀同一类有多个@Before
java
package com.example.bookslistdemo.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class TimeAspect {
@Pointcut("execution(* com.example.bookslistdemo.controller.*.*(..))")
private void pt(){}
@Before("pt()")
public void doBefore4(){
log.info("执行Before4方法");
}
@Before("pt()")
public void doBefore2(){
log.info("执行Before2方法");
}
@Before("pt()")
public void doBefore3(){
log.info("执行Before3方法");
}
@After("pt()")
public void doAfter(){
log.info("执行After方法");
}
@AfterReturning("pt()")
public void doAfterReturning(){
log.info("执行AfterReturning方法");
}
@AfterThrowing("pt()")
public void doAfterThrowing(){
log.info("执行AfterThrowing方法");
}
@Around("pt()")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
log.info("Around 方法开始执行");
Object result=pjp.proceed();
log.info("Around 方法结束执行");
return result;
}
}
访问后执行

发现我们的代码其实是按doBefore4--doBefore2--doBefore3的顺序方法名来的,但是执行是按
2--3--4来的,大家注意一下这种情况好吧。我只是给大家演示一下。
🍀不同类有多个@Before
类1

类2

类3

访问后观察

很明显它就是按数字的顺序来执行的,但我想说的是,它也按字母abcd的先执行!!
总结:@Before注解字母越小越先执行
@After注解字母越大越先执行
然后加上@Order注解试试



结果:

发现@Order里数字越小的,@Before越先执行
@Order里数字越大的@After越先执行!!
🍁切点表达式
第一个*表示返回类型,后面两个*都是表示一个占位,大家应该清楚,类.方法,最后面的那个(. .)表示无参和有参都行,且无论参数个数。
java
("execution(* com.example.bookslistdemo.controller.*.*(..))")
那如果我改一下
java
("execution(* com.example.bookslistdemo.controller..*(..))")
呢,表示controller下的所有子孙都匹配。
具体就这么些。
🍁@annotation
🍀1.自定义注解

@Target(ElementType.METHOD)表示修饰的是方法,
@Retention(RetentionPolicy.RUNTIME)表示是运行时存在。
🍀2.切面类

🍀3.在目标方法上添加自定义注解

执行结果

所以咋们一个自定义注解想加到哪个方法上哪个方法就生效,是不是很香。
但我们还可以在切面类里面改成下面这样--用@PointCut注解

结果也是一样的。
本篇博客完结,啦。