【Spring】深入解析 Spring AOP 核心概念:切点、连接点、通知、切面、通知类型和使用 @PointCut 定义切点的方法


Spring AOP


下面我们再来详细学习 AOP,主要是以下几部分:


Spring AOP****核心概念



切点**(Pointcut)**


切点(Pointcut),也称之为"切入点"。

Pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述),告诉程序对哪些方法来进行功能增强。

上面的表达式 execution(* com.example.demo.controller.*.*(..)) 就是切点表达式。


连接点**(Join Point)**


满足切点表达式规则方法,就是连接点。也就是可以被 AOP 控制的方法。

以入门程序举例,所有 com.example.demo.controller 路径下的方法,都是连接点。

上述 BookController 中的方法都是连接点。



切点和连接点的关系:

  • 连接点是满足切点表达式元素切点可以看做是保存了众多连接点的一个集合。 比如:
  • 切点表达式:全体偶像练习生
  • 连接点就是:蔡徐坤、范丞丞等各个偶像练习生

通知**(Advice)**


通知就是具体要做的工作,指哪些重复的逻辑,也就是共性功能最终体现为一个方法)。

比如上述程序中记录业务方法的耗时时间,就是通知

在 AOP 面向切面编程当中,我们把这部分重复的代码逻辑抽取出来单独定义,这部分代码就是通知的内容。


切面**(Aspect)**


切面(Aspect)= 切点(Pointcut)+ 通知(Advice)。

通过切面就能够描述当前 AOP 程序需要针对于哪些方法,在什么时候执行什么样的操作。

切面既包含了通知逻辑的定义,也包括了连接点的定义。

切面所在的类,我们一般称为切面类(被 @Aspect 注解标识的类)。


通知类型


上面我们讲了什么是通知,接下来学习通知的类型@Around 就是其中一种通知类型,表示环绕通知


Spring 中 AOP通知类型有以下几种:

注解名称 描述
@Around 环绕通知,此注解标注的通知方法在目标方法前、后都被执行。
@Before 前置通知,此注解标注的通知方法在目标方法前被执行。
@After 后置通知,此注解标注的通知方法在目标方法后被执行的,无论是否有异常都会执行。
@AfterReturning 返回后通知,此注解标注的通知方法在目标方法后被执行的,有异常不会执行。
@AfterThrowing 异常后通知,此注解标注的通知方法在发生异常后执行。

接下来我们通过代码来加深对这几个通知的理解:

为方便学习,我们可以新建一个项目,使用 web+lombok 依赖,删除 pom.xml 文件多余部分:


添加 AOP 依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

先创建测试类接口:


添加连接点:


创建切面(需引入 AOP 依赖):

java 复制代码
@Slf4j
@Aspect
@Component
public class AspectDemo1 {
    /**
     * @Around(环绕通知):此注解标注的通知方法在目标执行前,后都被执行
     */
    @Around("execution(* com.bit.springaopdemo.Controller.*.*(..))")
    public Object TimeAspect(ProceedingJoinPoint pjp) throws Throwable {
        log.info("目标方法执行前.......");
        Object result = pjp.proceed();
        log.info("目标方法执行后.......");
        return result;
    }

    /**
     * @Before(前置通知),此注解标注的通知方法在目标方法前被执行
     */
    @Before("execution(* com.bit.springaopdemo.Controller.*.*(..))")
    public void doBefore(){
        log.info("doBefore.......");
    }

    /**
     * @After(后置通知),此注解标注的通知方法在目标方法后被执行的,无论是否有异常都会执行。
     */
    @After("execution(* com.bit.springaopdemo.Controller.*.*(..))")
    public void doAfter(){
        log.info("doAfter.......");
    }

    /**
     * @AfterReturning(返回后通知),此注解标注的通知方法在目标方法后被执行的,有异常不会执行。
     */
    @AfterReturning("execution(* com.bit.springaopdemo.Controller.*.*(..))")
    public void doAfterReturning(){
        log.info("AfterReturning.......");
    }

    /**
     * @AfterThrowing(异常后通知),此注解标注的通知方法在发生异常后执行。
     */
    @AfterThrowing("execution(* com.bit.springaopdemo.Controller.*.*(..))")
    public void doAfterThrowing(){
        log.info("AfterThrowing.......");
    }
}

运行程序,观察日志:


1、正常运行的情况:


http://127.0.0.1:8080/test/t1


观察日志:


这个日志中,我们还是不知道目标方法 t1() 是什么时候执行的,再打上一个日志,重新运行程序,并发送请求:


程序正常运行的情况下,@AfterThrowing 标识的通知方法不会执行。

从上图也可以看出来,@Around 标识的通知方法包含两部分,一个"前置逻辑",一个"后置逻辑"


其中"前置逻辑"会先于 @Before 标识的通知方法执行,"后置逻辑"会晚于 @After 标识的通知方法执行:


2、异常时的情况:


为了方便观察,我们给 Controller 中的每个接口都加上 log 日志:


测试方法 t1() 是没有异常的情况,接下来,我们来测试目标方法存在异常的情况:


观察日志:


程序发生异常的情况下:

  • @AfterReturning 标识的通知方法不会执行;

  • @AfterThrowing 标识的通知方法执行了;


如果我们在环绕通知 @Around 中,使用 try....catch .....连接点(目标方法)的异常进行捕获,能否达到 @AfterThrowing 的功能呢?


@Around 环绕通知中原始方法调用时有异常,通知中的环绕后的代码逻辑也不会再执行了(因为原始方法调用出异常了)。



注意事项:

  • @Around 环绕通知需要调用 ProceedingJoinPoint.proceed() 来让原始方法执行其他通知不需要考虑目标方法执行

  • @Around 环绕通知 的方法返回值,必须指定为 Object ,来接收原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的。

  • 一个切面类可以有多个切点


定义切点**@PointCut**


上面代码存在一个问题,就是存在大量重复的切点表达式:

execution(* com.example.demo.controller.*.*(..))


Spring 提供了 @PointCut 注解,把公共的切点表达式提取出来,需要用到时引用该切入点表达式即可。

上述代码就可以修改为:



当切点定义使用 private 修饰时,仅能在当前切面类中使用。


其他切面类也要使用当前切点定义时,就需要把 private 改为 public


引用方式为:全限定类名.方法名()


相关推荐
wjcurry26 分钟前
我的实习日报
java·redis·mysql
我喜欢山,也喜欢海1 小时前
Jenkins Maven 带权限 搭建方案2025
java·jenkins·maven
明天更新1 小时前
Java处理压缩文件的两种方式!!!!
java·开发语言·7-zip
铁锚1 小时前
一个WordPress连续登录失败的问题排查
java·linux·服务器·nginx·tomcat
撸猫7911 小时前
HttpSession 的运行原理
前端·后端·cookie·httpsession
yychen_java2 小时前
上云API二开实现三维可视化控制中心
java·无人机
理智的煎蛋2 小时前
keepalived+lvs
java·开发语言·集成测试·可用性测试
CopyLower2 小时前
Java与AI技术结合:从机器学习到生成式AI的实践
java·人工智能·机器学习
生命不息战斗不止(王子晗)2 小时前
mybatis中${}和#{}的区别
java·服务器·tomcat