Java AOP 介绍与实践
天行健,君子以自强不息;地势坤,君子以厚德载物。
每个人都有惰性,但不断学习是好好生活的根本,共勉!
文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。
三山半落青天外,二水中分白鹭洲。
——《登金陵凤凰台》
文章目录
- Java AOP 介绍与实践
- 1. 介绍
- 2. AOP启动类注解(@EnableAspectJAutoProxy)
- 3. AOP切面类注解(@Aspect)
- 4. 通知(Advice)
- 4.1 环绕通知(@Around)
- 4.2 前置通知(@Before)
- 4.3 后置通知(@After)
- 4.4 返回通知(@AfterReturning)
- 4.5 报错通知(@AfterThrowing)
- 4.6 TIPS
- 4.7 Advice通知顺序
- 5. 连接点(JoinPoint)
- 5.1 JoinPoint获取信息(方法)
- 5.2 JoinPoint的子接口ProceedingJoinPoint
- 6. 目标对象(TargetObject)
- 7. AOP代理(AOP Proxy)
- 8. 切入点(Pointcut)
- 8.1 注解pointcut
- 8.2 切点表达式
- 8.2.1 方法表达式execution
- 8.2.1.1 表达式语法和参数说明
- 8.2.1.2 常用表达式举例
- 8.2.1.2.1 拦截指定包下所有类的所有方法(不包含该包下的子包)
- 8.2.1.2.2 拦截指定包下所有类的所有方法(包含该包下的子包)
- 8.2.1.2.3 拦截指定包下一级子包的所有类的所有方法(不包含该包下的子包下的子包)
- 8.2.1.2.4 拦截当前包下指定类的所有方法
- 8.2.1.2.5 拦截指定包下指定类的所有公共(public)方法
- 8.2.1.2.6 拦截指定包下指定类的所有返回值类型为Integer的方法
- 8.2.1.2.7 拦截指定包下指定类的所有入参第一个参数为String的方法
- 8.2.1.2.8 拦截所有带有指定注解的所有方法
- 8.2.1.3 组合表达式
- 8.2.1.3.1 与&&
- 8.2.1.3.2 或||
- 8.2.1.3.3 非!
- 8.2.1.4 注意事项
- 8.2.2 类型表达式within
- 8.2.3 注解类型表达式
- 8.2.4 匹配bean表达式
- 8.2.5 组合表达式
- 8.2.6 小结
- 9. AOP实践
- 9.1 依赖引入
- 9.2 完整依赖
- 9.3 项目包结构
- 9.4 项目启动类
- 9.5 切面类
- 9.2.1 类创建
- 9.2.2 切点实现
- 9.2.3 前置通知
- 9.2.4 环绕通知
- 9.2.5 后置通知
- 9.2.6 返回通知
- 9.2.7 报错通知
- 9.2.8 完整切面类代码
- 9.3 请求接口的实现
- 9.3.1 AopTestController.java
- 9.3.2 AopTest2Controller.java
- 9.3.3 AopTest3Controller.java
- 9.4 测试接口
- 9.4.1 AopTestController.java的方法
- 9.4.2 AopTest2Controller.java的方法
- 9.4.3 AopTest3Controller.java的方法
- 9.5 输出打印
- 9.5.1 test1输出结果
- 9.5.2 test2输出结果
- 9.5.3 test3输出结果
- 9.6 总结
- 加分加分
- 1. 加分
- 2. 加分
- 3. 加分
- 4. 加分
- 4.1 加分1
- 4.2 加分2
- 4.3 加分3
- 4.4 加分4
Java AOP 介绍与实践
1. 介绍
通常用于项目全局的日志管理、权限校验等
2. AOP启动类注解(@EnableAspectJAutoProxy)
在项目启动类上添加AOP注解,开启AOP的AspectJ自动代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
启动类...
3. AOP切面类注解(@Aspect)
通知Advice和切入点pointcut共同组成了切面
使用Aspect注解标注一个类(bean)后,spring框架自动收集bean,添加到spring aop中
但是仅仅使用Aspect注解并不能将一个Java对象转为bean,还是需要使用Component注解将其转为Java的bean
@Aspect
@Component
public class AspectTest{}
4. 通知(Advice)
通知Advice表示切面什么时候启用
通知类型有以下几种
环绕通知Around、前置通知Before、后置通知After、返回通知AfterReturning、报错通知AfterThrowing
4.1 环绕通知(@Around)
包围一个连接点的通知,通常在核心方法的前后实现自定义的逻辑
该注解需要传入参数ProceedingJoinPoint proceedingJoinPoint
决定是否进入核心方法,进入则调用proceedingJoinPoint.proceed(),不调用则不进入
4.2 前置通知(@Before)
前置通知,在核心代码执行前通知(执行代码前提前做一些逻辑行为,一般为权限校验等)
4.3 后置通知(@After)
后置通知,在核心代码执行后执行的通知(核心代码执行结束后进行通知,不管代码执行是否报错,都会通知)
4.4 返回通知(@AfterReturning)
返回后通知,正常返回后才会通知
4.5 报错通知(@AfterThrowing)
报错通知,抛出异常时通知,当核心代码报错时进行通知
4.6 TIPS
只有@Around的参数时ProceedingJoinPoint,其余的通知的参数都是JoinPoint
也就是说,能进入核心代码的只有@Around
aop进入核心代码是通过调用ProceedingJoinPoint的proceed()方法,JoinPoint在无此方法
4.7 Advice通知顺序
一个方法被一个Aspect切面拦截时,顺序如下(根据代码执行结果查看得出)
Around1
Before
核心代码执行
AfterReturning
After
Around2
首先执行环绕通知
然后执行前置通知
然后执行核心代码
然后执行返回通知(如果代码报错则执行报错通知,返回和报错只会出现一个)
然后执行后置通知
然后执行环绕通知
一个方法被两个Aspect切面拦截时,顺序会根据系统随机执行某个切面的通知,但同一个切面的通知顺序是不会变的还跟上面一样
可设置Order参数定义优先级
如在Aspect切面1的类上定义Order(1),则会优先执行该切面
5. 连接点(JoinPoint)
aop拦截的类或者方法,如当方法被调用或异常抛出时
Java spring中的所有方法都可以看作joinpoint,但并不是多有的方法都需要aop拦截添加advice,pointcut就是提供一组规则来匹配所需的joinpoint(方法)
对匹配的方法添加Advice
5.1 JoinPoint获取信息(方法)
JoinPoint是AspectJ框架中用于表示目标类连接点对象的一个接口
Spring AOP中任何增强方法都可以通过将第一个入参声明为JoinPoint来访问连接点上下文的信息
JoinPoint接口提供以下几个主要的方法
getAgrgs() 获取连接点方法运行时的入参列表
getSignature() 获取连接点的方法签名对象
getTarget() 获取连接点所在的目标对象
getThis() 获取代理对象本身
getKind()
getSourceLocation()
getStaticPart()
getClass()
......
5.2 JoinPoint的子接口ProceedingJoinPoint
ProceedingJoinPoint是JoinPoint接口的子接口
只有在环绕通知@Around时使用ProceedingJoinPoint
在环绕通知中可使用ProceedingJoinPoint的proceed()方法进入核心代码
ProceedingJoinPoint包含了被通知方法的执行信息,同时可以访问被通知方法的信息和参数
可以通过使用ProceedingJoinPoint接口来实现更加灵活和精细的环绕通知逻辑
可以使用proceed()方法执行被通知的方法,该方法返回的值类型可能多种类型,可用Object接收
在proceed()方法前后分别可用于前置逻辑和后置逻辑的实现
如果环绕通知中不使用proceed()方法,则被通知的方法不会执行
ProceedingJoinPoint的方法和JoinPoint的类似,可参考使用
6. 目标对象(TargetObject)
被通知的对象
7. AOP代理(AOP Proxy)
AOP代理有两种,JDK动态代理和CGLIB代理
默认情况下TargetObject实现接口时使用JDK动态代理,没有实现接口则使用CGLIB代理
8. 切入点(Pointcut)
匹配joinPoint,决定切面切在哪,通常切入类或者包路径
8.1 注解pointcut
在切面类中定义切入点方法,方法名可自定义,在该方法上使用切入点注解
切入点注解的值可以控制切入的方法范围,如不同类的不同方法等
/**
* 定义切点,指定应用的类包和方法
* 第一个*代表返回类型不限
* 第二个*代表所有类,加上Controller表示所有以Controller结尾名字的类
* 第三个*代表所有方法
* 最后的(..)代表参数不限
*/
@Pointcut(value = "execution(public * com.aop.*.*Controller.*(..))")
public void pointcut(){}
8.2 切点表达式
切点表达式就是放入切点point注解中的值
8.2.1 方法表达式execution
https://blog.csdn.net/hyc010110/article/details/141710551
使用execution(方法表达式)进行匹配方法执行
8.2.1.1 表达式语法和参数说明
语法
execution([修饰符] 返回类型 [包.类.方法] (参数))
其中方括号[]中的参数是可以加上也可以不加
参数说明
修饰符
可选,指定方法的访问修饰符,如public、private等返回类型
必填,指定方法的返回类型,可用*表示匹配任意类型包.类.方法
可选,指定类和方法的全限定名,*表示匹配任意类型或者方法参数
必填,指定方法的参数列表,…表示匹配任意参数
8.2.1.2 常用表达式举例
8.2.1.2.1 拦截指定包下所有类的所有方法(不包含该包下的子包)
如拦截com.aop.controller包下的所有类的方法
表达式
execution(* com.aop.controller.*.*(..))
说明
匹配指定包中的所有方法
第一个*表示所有返回值类型
com.aop.controller是包的全限定名
第二个*是所有类,第三个*是所有方法
最后的(..)是参数,表示任意参数
8.2.1.2.2 拦截指定包下所有类的所有方法(包含该包下的子包)
如拦截com.aop包下的所有类的方法,包含其子包下的类的所有方法
如com.aop.common包下的类的所有方法
com.aop.controller包下的类的所有方法
com.aop.common.test包下的类的所有方法
等等
都会被拦截到
表达式
execution(* com.aop..*.*(..))
或者直接省略类的表示
execution(* com.aop..*(..))
说明
匹配指定包中的所有方法,包含其子包下的类的所有方法
第一个*表示所有返回值类型
com.aop是包的全限定名
第二个*是所有类,第三个*是所有方法
最后的(..)是参数,表示任意参数
8.2.1.2.3 拦截指定包下一级子包的所有类的所有方法(不包含该包下的子包下的子包)
如拦截com.aop包下一级子包的所有类的方法,不包含一级子包下的子包的类的所有方法
如com.aop.common包下的类的所有方法
com.aop.controller包下的类的所有方法
等等
都会被拦截到
而com.aop包下子包的子包则不会拦截
如com.aop.common.test包下的类的所有方法不会被拦截
表达式
execution(* com.aop.*.*.*(..))
说明
匹配指定包中的所有方法,包含其子包下的类的所有方法
第一个*表示所有返回值类型
com.aop.*是包的全限定名
第二个*是aop包下一级的所有包,不含其下一级以下的子包
第三个*是所有类,第四个*是所有方法
最后的(..)是参数,表示任意参数
8.2.1.2.4 拦截当前包下指定类的所有方法
当前包指的是切面类所在的包
如切面类AopAspectJ.java类在com.aop.common包下,则该包就是当前包,此时匹配时无需加包名
如拦截当前包下的指定类AopTest2Controller.java的所有方法
表达式
execution(* AopTest2Controller.*(..))
也可使用*模糊匹配类名
如前缀匹配,匹配以AopTest2开头的类名
execution(* AopTest2*.*(..))
后缀匹配,匹配以2Controller结尾的类名
execution(* *2Controller.*(..))
8.2.1.2.5 拦截指定包下指定类的所有公共(public)方法
方法修饰符可以是public、private等,可根据修饰符不同进行匹配
如拦截com.aop.common包下AopTest2Controller.java类中所有public修饰的方法
表达式
execution(public * com.aop.common.AopTest2Controller.*(..))
8.2.1.2.6 拦截指定包下指定类的所有返回值类型为Integer的方法
返回值类型可以是很多种类型,如String、Integer、Object等
如拦截com.aop.common包下AopTest2Controller.java类中所有返回类型为Integer的方法
表达式
execution(Integer com.aop.common.AopTest2Controller.*(..))
这里需要注意,使用的是Integer而非int,我这里试了下int会报错,你可以测试一下,暂时不清楚原因
8.2.1.2.7 拦截指定包下指定类的所有入参第一个参数为String的方法
方法的参数列表可以多样的,可精准匹配指定入参的方法
如拦截com.aop.common包下AopTest2Controller.java类中所有第一个入参为String类型的方法
表达式
execution(* com.aop.common.AopTest2Controller.*(String, ..))
8.2.1.2.8 拦截所有带有指定注解的所有方法
拦截带有某个注解的所有方法
如拦截带有@Scheduled注解的所有方法,注解需要使用全限定名(即@加上import后面跟的全部)
所有带有@Scheduled注解的方法在自动执行时都会被拦截
表达式
execution(@org.springbframework.scheduling.annotation.Scheduled * *(..))
8.2.1.3 组合表达式
通过与或非(and/or/not)进行组合表达式拦截
8.2.1.3.1 与&&
使用&&来实现同时满足两个或多个条件的
如
execution(* com.aop.common.AopTest2Controller.*(..)) && execution(* com.aop.controller.AopTestController.*(..))
8.2.1.3.2 或||
使用||来实现多个条件匹配(满足其一即可)
如拦截com.aop.common.AopTest2Controller类或com.aop.controller.AopTestController下的所有方法
execution(* com.aop.common.AopTest2Controller.*(..)) || execution(* com.aop.controller.AopTestController.*(..))
8.2.1.3.3 非!
使用!来实现排除某种情况之外的部分执行(非的符号位英文感叹号!)
如拦截com.aop.common.AopTest2Controller类所有方法以外的所有方法
!execution(* com.aop.common.AopTest2Controller.*(..))
8.2.1.4 注意事项
注意1:
在使用表达式时,包的路径在开始包com后至少加上一级包名,后面才可以使用*
进行模糊匹配,即,开始都需要以com.aop开始(我这里com下面是aop,你根据自己的包名进行使用),后面进行模糊匹配定义
如com.aop.*.*.*(..)
,每一个*
代表一级包,最后的两个*
表示类和方法
..
表示多级包,如com.aop..*(..)
,表示aop下的所有方法包括其子包
注意2:
在使用带有Integer的表达式时,在方法的返回值类型上,需定义包装类,也就是Integr,而不是int,不然会报错(org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public int com.aop.common.Test4Controller.test1())
注意3:
在使用最后一个定时任务注解时,需要先参考文章实现定时任务功能,再进行aop的配置
Java定时任务@Scheduled的使用
8.2.2 类型表达式within
使用within(类型表达式)匹配指定类型内的方法执行
//匹配指定包(controller)中的所有方法,但不包括子包
within(com.aop.controller.*)
//匹配指定包controller中的所有方法,包括子包
within(com.aop.controller..*)
//匹配当前包(即和切面类同包)中的指定类中的方法
within(Test8)
//匹配当前包(即和切面类同包)中的一个接口的所有实现类中的方法,(实现类可以不在当前包,接口必须在)
within(Test9Interface+)
//匹配只当包中的指定接口的所有实现类中的方法
within(com.aop.common.Test9Interface+)
8.2.3 注解类型表达式
@within
使用@within注解类型匹配所有持有指定注解类型内的方法,注解类型必须是全限定类型名
//匹配所有持有指定注解类型内的方法(如@RestController)
@within(org.springframework.web.bind.annotation.RestController)
注意:在目标对象(类)有此注解才能生效,在接口上声明的不会生效
@annotation
使用@annotation注解类型匹配当前执行方法持有指定注解的方法,注解类型必须时全限定类型名
//当前执行方法上持有注解@GetMapping则匹配
@annotation(org.springframework.web.bind.annotation.GetMapping)
8.2.4 匹配bean表达式
匹配以指定名字结尾的Bean中的所有方法
如匹配名称以st11结尾的bean中的所有方法,所有类上加RestController/Controller/Component等都会将类以bean形式管理,类名即bean名
bean(*st11)
8.2.5 组合表达式
以上表达式都可以使用与(&&)、或(||)、非(!)等符号进行混合使用
controller包下所有方法或当前包下Test8类中的所有方法
within(com.aop.controller..*||Test8)
或
within(com.aop.controller..*)||within(Test8)
controller包下所有被GetMapping注解的方法
execution(* com.aop.controller.*.*(..))||@annotation(org.springframework.web.bind.annotation.GetMapping)
8.2.6 小结
execution一般用于匹配方法使用,within通常用于匹配注解
9. AOP实践
以下为AOP的简单实现
本项目demo以springboot框架实现
9.1 依赖引入
引入aop的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.18</version>
</dependency>
当然了,除了aop的依赖,本demo中还用到了其他依赖
9.2 完整依赖
项目的所有依赖如下
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hslb</groupId>
<artifactId>aop_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.18</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
</dependencies>
</project>
9.3 项目包结构
该demo的包结构如下,可自行创建,主要为了测试aop匹配不同层级的包的情况
9.4 项目启动类
AopApplication.java
package com.aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @ClassDescription: 项目启动类
* 使用@EnableAspectJAutoProxy(proxyTargetClass = true)开启aop
* @JdkVersion: 1.8
* @Author: 李白
* @Created: 2024/12/21 14:33
*/
@EnableAspectJAutoProxy(proxyTargetClass = true)
@SpringBootApplication
public class AopApplication {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
}
}
9.5 切面类
以下切面类分多个部分进行拆分实现,最后有完整的切面类代码
9.2.1 类创建
创建切面类AopAspectJ.java
并在类上使用注解@Aspect标记该类为切面类,同时需要使用@Component注解将其标记为Bean
package com.aop.common;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @ClassDescription: aop切面类
* @JdkVersion: 1.8
* @Author: 李白
* @Created: 2024/12/21 14:35
*/
@Aspect
@Component
public class AopAspectJ {
}
9.2.2 切点实现
在AopAspectJ.java类中定义一个切点方法
方法修饰符为public
返回值为void
方法名自定义
在方法上使用@Pointcut注解将其标记为切点方法
同时在注解中将匹配条件填入,以此来进行拦截满足条件的方法
/**
* 定义切入点方法,方法名自定义,无参数,无内容,定义的方法名后续注解需要使用,以此方法切入
*/
// @Pointcut(value = "execution(* com.aop..*(..))")
// @Pointcut(value = "execution(* com.aop..*.*(..))")
// @Pointcut(value = "execution(* com.aop.controller.*.*(..))")
@Pointcut(value = "execution(* com.aop.*.*.*(..))")
public void pointcut(){}
9.2.3 前置通知
在AopAspectJ.java类中定义前置通知,进行方法执行前的逻辑实现
/**
* 定义前置通知@Beafore
*/
@Before(value = "pointcut()")
public void before(){
System.out.println("前置通知@Before------------------>");
}
9.2.4 环绕通知
在AopAspectJ.java类中定义环绕通知,进行方法执行前、中、后的逻辑实现
/**
* 定义环绕置通知@Around
* @param proceedingJoinPoint 连接点
* @throws Throwable 抛错
*/
@Around(value = "pointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//前置逻辑
System.out.println("环绕通知@Around 执行前------------------>");
//执行被通知的方法
proceedingJoinPoint.proceed();
//后置逻辑
System.out.println("环绕通知@Around 执行后------------------>");
}
9.2.5 后置通知
在AopAspectJ.java类中定义后置通知,进行方法执行后的逻辑实现
/**
* 定义后置通知@After
*/
@After(value = "pointcut()")
public void after(){
System.out.println("后置通知@After------------------>");
}
9.2.6 返回通知
在AopAspectJ.java类中定义返回通知,进行方法执行后并成功返回后的逻辑实现
/**
* 定义返回通知@AfterReturning
* @param joinPoint 连接点
* @param result 返回结果
* @return 返回
*/
@AfterReturning(value = "pointcut()", returning = "result")
public JSONObject afterReturning(JoinPoint joinPoint, JSONObject result){
System.out.println("返回通知@AfterReturning------------------>");
return null;
}
9.2.7 报错通知
在AopAspectJ.java类中定义报错通知,进行方法执行失败后的逻辑实现
/**
* 定义报错通知@AfterThrowing
*/
@AfterThrowing(value = "pointcut()")
public void afterThrowing(){
System.out.println("报错通知@AfterThrowing------------------>");
}
9.2.8 完整切面类代码
以下为切面类的完整代码实现
AopAspectJ.java
package com.aop.common;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @ClassDescription: aop切面类
* @JdkVersion: 1.8
* @Author: 李白
* @Created: 2024/12/21 14:35
*/
@Aspect
@Component
public class AopAspectJ {
/**
* 定义切入点方法,方法名自定义,无参数,无内容,定义的方法名后续注解需要使用,以此方法切入
*/
// @Pointcut(value = "execution(* com.aop..*(..))")
// @Pointcut(value = "execution(* com.aop..*.*(..))")
// @Pointcut(value = "execution(* com.aop.controller.*.*(..))")
@Pointcut(value = "execution(* com.aop.*.*.*(..))")
public void pointcut(){}
/**
* 定义前置通知@Beafore
*/
@Before(value = "pointcut()")
public void before(){
System.out.println("前置通知@Before------------------>");
}
/**
* 定义环绕置通知@Around
* @param proceedingJoinPoint 连接点
* @throws Throwable 抛错
*/
@Around(value = "pointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//前置逻辑
System.out.println("环绕通知@Around 执行前------------------>");
//执行被通知的方法
proceedingJoinPoint.proceed();
//后置逻辑
System.out.println("环绕通知@Around 执行后------------------>");
}
/**
* 定义后置通知@After
*/
@After(value = "pointcut()")
public void after(){
System.out.println("后置通知@After------------------>");
}
/**
* 定义返回通知@AfterReturning
* @param joinPoint 连接点
* @param result 返回结果
* @return 返回
*/
@AfterReturning(value = "pointcut()", returning = "result")
public JSONObject afterReturning(JoinPoint joinPoint, JSONObject result){
System.out.println("返回通知@AfterReturning------------------>");
return null;
}
/**
* 定义报错通知@AfterThrowing
*/
@AfterThrowing(value = "pointcut()")
public void afterThrowing(){
System.out.println("报错通知@AfterThrowing------------------>");
}
}
9.3 请求接口的实现
在不同的包中创建请求接口,接口类中的请求路径不同且输出也不同,然后通过表达式不同的拦截条件进行测试不同的接口
查看输出内容即可验证
9.3.1 AopTestController.java
com.aop.controller.AopTestController.java类
package com.aop.controller;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
/**
* @ClassDescription: 请求测试类
* @JdkVersion: 1.8
* @Author: 李白
* @Created: 2024/12/21 14:34
*/
@RestController
@RequestMapping(value = "/aop")
public class AopTestController {
@GetMapping(value = "test1")
public JSONObject test1(@RequestHeader("token")String token,
@RequestParam("username")String username){
JSONObject jsonObject = new JSONObject();
jsonObject.put("token", token);
jsonObject.put("username", username);
System.out.println("aop test1 token: "+token+" username: "+username);
return jsonObject;
}
}
9.3.2 AopTest2Controller.java
com.aop.common.AopTest2Controller.java类
package com.aop.common;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
/**
* @ClassDescription: 请求测试类
* @JdkVersion: 1.8
* @Author: 李白
* @Created: 2024/12/21 14:34
*/
@RestController
@RequestMapping(value = "/aop2")
public class AopTest2Controller {
@GetMapping(value = "test2")
public JSONObject test1(@RequestHeader("token")String token,
@RequestParam("username")String username){
JSONObject jsonObject = new JSONObject();
jsonObject.put("token", token);
jsonObject.put("username", username);
System.out.println("aop test2 token: "+token+" username: "+username);
return jsonObject;
}
}
9.3.3 AopTest3Controller.java
com.aop.common.test.AopTest3Controller.java类
package com.aop.common.test;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
/**
* @ClassDescription: 请求测试类
* @JdkVersion: 1.8
* @Author: 李白
* @Created: 2024/12/21 14:34
*/
@RestController
@RequestMapping(value = "/aop3")
public class AopTest3Controller {
@GetMapping(value = "test3")
public JSONObject test1(@RequestHeader("token")String token,
@RequestParam("username")String username){
JSONObject jsonObject = new JSONObject();
jsonObject.put("token", token);
jsonObject.put("username", username);
System.out.println("aop test3 token: "+token+" username: "+username);
return jsonObject;
}
}
9.4 测试接口
启动项目
使用postman进行请求接口测试
依次调用三个接口
9.4.1 AopTestController.java的方法
请求方式
Get
请求地址
127.0.0.1:8080/aop/test1
请求头参数
请求参数
9.4.2 AopTest2Controller.java的方法
请求方式
Get
请求地址
127.0.0.1:8080/aop2/test2
请求参数和值都与上一个接口一样
9.4.3 AopTest3Controller.java的方法
请求方式
Get
请求地址
127.0.0.1:8080/aop3/test3
请求参数和值都与上一个接口一样
9.5 输出打印
在postman中分别调用三个接口
控制台输出内容依次如下
9.5.1 test1输出结果
AopTestController.java中test1的输出结果如下
环绕通知@Around 执行前------------------>
前置通知@Before------------------>
aop test1 token: 13579 username: libai
返回通知@AfterReturning------------------>
后置通知@After------------------>
环绕通知@Around 执行后------------------>
9.5.2 test2输出结果
AopTest2Controller.java中test1的输出结果如下
环绕通知@Around 执行前------------------>
前置通知@Before------------------>
aop test2 token: 13579 username: libai
返回通知@AfterReturning------------------>
后置通知@After------------------>
环绕通知@Around 执行后------------------>
9.5.3 test3输出结果
AopTest3Controller.java中test1的输出结果如下
aop test3 token: 13579 username: libai
9.6 总结
因为我们的表达式是execution(* com.aop.*.*.*(..))
该表达式拦截的是com.aop包下一级包中的所有类的所有方法,不包含下一级包的子包
test1和test2都是aop下一级包的类中的方法
test1是aop.controller包下的类的方法
test2是aop.common包下的类的方法
而test3是aop.common.test包下类的方法,已经是aop包下一级包common包的子包test包下的方法,多了一级,因此拦截不到
感谢阅读,祝君暴富!
版权声明:
- 作者:寒山李白
- 博客地址:https://hanshan.blog.csdn.net/
- 版权:本作品采用《创作共享许可证》进行许可,根据该许可授权的内容可在符合本许可证条款的前提下自由使用、、修改和创作衍生作品。
版权许可介绍:
本文采用CC BY-NC-SA许可证
此许可允许在使用者仅出于非商业目的以任何媒体或格式分发、重新混合、改编和构建材料,并且前提是注明创作者。如果您重新混合、改编或基于该材料进行构建,则必须按照相同的条款对修改后的材料进行许可。
更多信息请访问以下网址查看:
版权官网 https://creativecommons.org/licenses/by-nc-sa/4.0/
中文翻译 https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans
文章结束
--------------------------------------------------------------------------------------------------------------------------------------
以下内容增加文章评分用,可忽略
加分加分
黄河之水天上来,奔流到海不复回
高堂明镜悲白发,朝如青丝暮成雪
1. 加分
黄河之水天上来,奔流到海不复回
高堂明镜悲白发,朝如青丝暮成雪
2. 加分
黄河之水天上来,奔流到海不复回
高堂明镜悲白发,朝如青丝暮成雪
3. 加分
寒山李白
寒山李白
4. 加分
账号密码
名称 | 密码 |
---|---|
李白 | 123 |
4.1 加分1
账号密码
名称 | 密码 |
---|---|
李白 | 123 |
4.2 加分2
账号密码
名称 | 密码 |
---|---|
李白 | 123 |
4.3 加分3
账号密码
名称 | 密码 |
---|---|
李白 | 123 |
4.4 加分4
账号密码
名称 | 密码 |
---|---|
李白 | 123 |