文章目录
前言:
Spring版本不一样,通知执行顺序也会存在差异
查看Spring版本的方法:
public static void main(String[] args) {
System.out.println(SpringVersion.getVersion());
}
版本结果:
新建一个springBoot项目,目录结构:
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.7</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>chang</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>chang</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
切点类代码:
定义切点时execution表达式语法规则:
execution表达式语法规则
package com.example.chang.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
//该注解表示声明该类为一个切面类
@Aspect
@Component
//可以使用@Order注解指定先后顺序,数字越小,优先级越高,先进后出
@Order(value = 1)
public class MyAspect {
//定义一个切点,execution表达式可以灵活运用
@Pointcut("execution(public * com.example.chang..*.*(..))")
public void pointCut(){};
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
System.out.println("进入@Before测试"+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+ Arrays.asList(args)+"}");
}
//方法执行开始之后
@After("pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println("进入@Afte测试"+joinPoint.getSignature().getName()+"结束。。。@After");
}
//当方法进行返回的时候,returning属性是指定方法参数中的result来接收返回参数,这样就可以修改返回参数
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println("进入@AfterReturning测试"+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
}
//当方法执行异常的时候,throwding是指定方法参数中的e来接收异常参数,可以查看发生的什么异常
@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println("进入@AfterThrowing测试"+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
}
//环绕通知
@Around("pointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("进入@Around测试"+joinPoint.getSignature().getName()+"开始");
//开始执行真正的请求
Object obj= joinPoint.proceed();
System.out.println("进入@Around测试"+joinPoint.getSignature().getName()+"中间");
System.out.println("进入@Around测试"+joinPoint.getSignature().getName()+"结束");
return obj;
}
}
测试类:
package com.example.chang.aop;
import org.springframework.core.SpringVersion;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("request")
public class TestController01 {
@RequestMapping("test")
public String test(){
System.out.println("真正的方法");
//System.out.println(6/0);
return "success666";
}
//可以查看自己的spring的版本
public static void main(String[] args) {
System.out.println(SpringVersion.getVersion());
}
}
启动项目
在浏览器中输入访问路径:
正常运行查看日志输出:
抛出异常查看日志输出:
敲黑板了!进行总结:
单个切面,通知的执行顺序:
(1)单个@Before、@After、@AfterReturning、@AfterThrowing执行顺序
①Spring4.0
正常情况:@Before=目标方法=@After=====@AfterReturning
异常情况:@Before=目标方法=@After=====@AfterThrowing
②Spring5
正常情况:@Before=目标方法=@AfterReturning=====@After
异常情况:@Before=目标方法=@AfterThrowing=====@After
(2)单个@Around的执行顺序
①Spring4.0
正常情况:环绕前置===== 目标方法执行===== 环绕返回===== 环绕最终
异常情况:环绕前置===== 目标方法执行===== 环绕异常===== 环绕最终
②Spring5
正常情况:环绕前置===== 目标方法执行===== 环绕返回===== 环绕最终
异常情况:环绕前置===== 目标方法执行===== 环绕异常===== 环绕最终
(3)五大通知执行顺序
①Spring4.0
正常情况:环绕前置=====@Before====== 目标方法执行===== 环绕返回===== 环绕最终=====@After=====@AfterReturning
异常情况:环绕前置=====@Before====== 目标方法执行===== 环绕异常===== 环绕最终===== @After=====@AfterThrowing
②Spring5
正常情况:环绕前置===== @Before=目标方法执行= @AfterReturning===== @After ===== 环绕返回===== 环绕最终
异常情况:环绕前置===== @Before=目标方法执行= @AfterThrowing===== @After ===== 环绕异常===== 环绕最终
多个切面,通知的执行顺序:
多个切面时,定义不同的@Order(value = ?)
@Aspect
@Component
//可以使用@Order注解指定先后顺序,数字越小,优先级越高,先进后出
@Order(value = 1)
public class MyAspect {}
@Aspect
@Component
//可以使用@Order注解指定先后顺序,数字越小,优先级越高,先进后出
@Order(value = 5)
public class MyAspect02 {}
①Spring4.0
正常情况:切面1环绕前置=== 切面1@Before=== 切面2环绕前置=== 切面2@Before=== 目标方法执行=== 切面2环绕返回=== 切面2环绕最终=== 切面2@After=== 切面2@AfterReturning=== 切面1环绕返回=== 切面1环绕最终=== 切面1@After=== 切面1@AfterThrowing
异常情况:切面1环绕前置=== 切面1@Before=== 切面2环绕前置=== 切面2@Before=== 目标方法执行=== 切面2环绕异常=== 切面2环绕最终=== 切面2@After=== 切面2@AfteThrowing === 切面1环绕异常=== 切面1环绕最终=== 切面1@After=== 切面1@AfterThrowing
②Spring5
正常情况:切面1环绕前置=== 切面1@Before=== 切面2环绕前置=== 切面2@Before=== 目标方法执行=== 切面2@AfterReturning=== 切面2@After=== 切面2环绕返回=== 切面2环绕最终=== 切面1@AfterReturning=== 切面1@After=== 切面1环绕返回=== 切面1环绕最终
异常情况:切面1环绕前置=== 切面1@Before=== 切面2环绕前置=== 切面2@Before=== 目标方法执行=== 切面2@AfterThrowing=== 切面2@After === 切面2环绕异常=== 切面2环绕最终=== 切面1@AfterThrowing=== 切面1@After=== 切面1环绕异常===切面1环绕最终
多个切面的示例代码:
把MyAspect类的代码复制,新建个类且@Order(value = 5),改一下输出的日志:
MyAspect02类代码:
package com.example.chang.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
//该注解表示声明该类为一个切面类
@Aspect
@Component
//可以使用@Order注解指定先后顺序,数字越小,优先级越高,先进后出
@Order(value = 5)
public class MyAspect02 {
//定义一个切点,execution表达式可以灵活运用
@Pointcut("execution(public * com.example.chang..*.*(..))")
public void pointCut(){};
//省略同MyAspect类的代码
}
运行结果
( 对应上面的 结论–》 多个切面,通知的执行顺序–》 Spring5 --》正常情况 )
参考:
执行顺序参考文件:https://www.cnblogs.com/orzjiangxiaoyu/p/13869747.html