当前位置: 首页 > article >正文

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


http://www.kler.cn/a/450374.html

相关文章:

  • UE5 渲染管线 学习笔记
  • Vue常用指令
  • opencv中的各种滤波器简介
  • Docker 技术系列之安装多版本Mysql5.6和Mysql5.7
  • 0009.基于springboot+layui的ERP企业进销存管理系统
  • Qt笔记:网络编程UDP
  • amazon广告授权
  • Django 模型管理器中自定义方法和添加导出功能
  • 聊聊volatile的实现原理?
  • conda 环境报错error while loading shared libraries: libpython3.9.so.1.0
  • 日志和MVCC的详解
  • JavaScript查缺补漏
  • Windows、CentOS环境下搭建自己的版本管理资料库:GitBlit
  • #渗透测试#漏洞挖掘#红蓝攻防#漏洞挖掘#未授权漏洞-Es未授权漏洞
  • 如何保障多个Facebook账号稳定运行:一账号一稳定IP?
  • Mac Android studio 升级LadyBug 版本,所产生的bug
  • 八股(One Day one)
  • 关于electron项目运行时,只编译渲染进程,不编译主进程问题
  • 前后端学习中本周遇到的内容
  • OpenHarmony怎么修改DPI密度值?RK3566鸿蒙开发板演示
  • 各种网站(学习资源及其他)
  • golang LeetCode 热题 100(动态规划)-更新中
  • Redis大Key问题全解析
  • 鸿蒙项目云捐助第二十讲云捐助项目物联网IOT的使用
  • python11-函数
  • NS3学习——tcpVegas算法代码详解(1)