首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring AOP 实践指南

Spring AOP 实践指南

作者头像
訾博ZiBo
发布于 2025-01-06 11:32:14
发布于 2025-01-06 11:32:14
17300
代码可运行
举报
运行总次数:0
代码可运行

Spring AOP 实践指南

一、概述

1、简介

Spring AOP(面向切面编程)是Spring框架的一个关键特性之一。它提供了一种在应用程序中实现横切关注点的方法,这些关注点通常会散布在应用程序的多个模块中,并且与核心业务逻辑存在交叉。

AOP通过将关注点从它们所影响的对象中分离出来,使得开发人员能够更好地关注业务逻辑的实现,而不必担心与之交织在一起的横切关注点。在Spring中,这些横切关注点可以包括日志记录、安全性、事务管理、性能监控等等。

Spring AOP的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和引入(Introduction)。

  • 切面(Aspect):切面是一个模块化单元,它是横切关注点的实现。它由切点和通知组成。
  • 连接点(Join Point):连接点是在应用程序执行期间可以插入切面的点。例如,方法调用、方法执行、异常处理等都可以是连接点。
  • 通知(Advice):通知是在连接点上执行的代码。它定义了在何时、何地和如何应用切面。常见的通知类型包括前置通知(Before)、后置通知(After)、返回通知(After Returning)和异常通知(After Throwing)。
  • 切点(Pointcut):切点定义了在何处应用通知。通过使用切点表达式,可以指定连接点的匹配规则。
  • 引入(Introduction):引入允许我们向现有的类添加新的接口和实现,以便可以将新功能引入到这些类中。

Spring AOP使用代理模式来实现横切关注点的管理。在运行时,Spring会动态地创建代理对象,将通知织入到目标对象的方法调用中。

通过使用Spring AOP,开发人员可以更好地实现关注点的模块化和重用,从而提高代码的可维护性和可扩展性。

2、官方资料

Spring 官网:https://spring.io/

Spring 文档:https://docs.spring.io/spring-framework/reference/

3、本文档说明

本文档基于 Spring Boot 以及注解使用 Spring AOP 功能。

二、基本使用

1、引入依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、定义切面

在 Spring 管理的 Bean 类上使用 @Aspect 注解就可以定义一个切面。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {
}

3、定义切点

在切面类的方法使用 @Pointcut 注解来定义切点,然后在通知注解中使用方法签名来指定切点。 切点表达式用来匹配切入的目标类和方法。目标类只能是 Spring 容器管理的类,切面只能切入 Bean 中的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param pjp 切点
     * @return Object
     * @throws Throwable 异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("before");
        Object result = pjp.proceed();
        System.out.println("after");
        return result;
    }
}

4、创建 HelloController

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zibo
 * @date 2023/5/15 12:55
 * @slogan 真正的大师永远怀着一颗学徒的心。——易大师
 */
@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/world")
    public String helloWorld() {
        System.out.println("hello world");
        return "Hello World!";
    }

}

5、启动项目,访问测试

访问地址:http://localhost:8080/hello/world

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 响应结果
Hello World!

# 控制台
before
Hello World!
after

三、通知

1、概述

五种通知

AOP 中的通知是基于连接点(Join point)业务逻辑的一种增强,Spring AOP 提供了下面五种通知类型:

  • Before advice(前置通知):连接点前面执行,不能终止后续流程,除非抛异常
  • After returning advice(后置通知):连接点正常返回时执行,有异常不执行
  • Around advice(环绕通知):围绕连接点前后执行,也能捕获异常处理
  • After advice(最终通知):连接点退出时执行,无论是正常退出还是异常退出
  • After throwing advice(异常通知):连接点方法抛出异常时执行

AOP 的连接点一般是指目标类的方法,五种通知类型执行的节点如下:

通知的顺序

Spring AOP 中一个目标类可以被多个切面切入,多个切面也可以切入一个目标类。

使用 @Order 注解来指定切面的优先级,来控制切面的执行顺序。

在注册切面 Bean 的时候指定 @Order,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Order(1)
@Aspect
@Component
public class FirstAspect {
    // ......
}

2、通知方法接受的参数

  1. JoinPoint:JoinPoint是Spring AOP中表示连接点的对象。它提供了访问连接点的信息,如目标对象、方法签名、方法参数等。通过JoinPoint参数,可以获取有关当前正在执行的连接点的信息。
  2. ProceedingJoinPoint:ProceedingJoinPoint是JoinPoint的一个子接口,它只在使用环绕通知时才会使用。它提供了proceed()方法,用于执行连接点方法。ProceedingJoinPoint参数可以用于在环绕通知中控制连接点方法的执行。
  3. org.aspectj.lang.JoinPoint.StaticPart:JoinPoint.StaticPart表示连接点的静态部分。它提供了与JoinPoint相同的信息,但不提供对连接点方法的执行控制。
  4. org.aspectj.lang.Signature:Signature表示连接点方法的签名。它提供了对方法名称、修饰符、返回类型、参数类型等的访问。
  5. org.aspectj.lang.ProceedingJoinPoint.StaticPart:ProceedingJoinPoint.StaticPart是ProceedingJoinPoint的静态部分。它提供了与ProceedingJoinPoint相同的信息,但不提供对连接点方法的执行控制。
  6. org.aspectj.lang.JoinPoint.EnclosingStaticPart:JoinPoint.EnclosingStaticPart表示连接点所在的静态部分。它提供了与JoinPoint相同的信息,但是可以用于获取连接点所在的类或切面的信息。

这些参数可以根据需要选择性地在通知方法中使用,以获取关于连接点和方法的相关信息。

3、前置通知

代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 前置通知
     *
     * @param jp 切点
     */
    @Before("pointcut()")
    public void beforeAdvice(JoinPoint jp) {
        System.out.println("前置通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
    }
}
控制台打印内容
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
前置通知
方法名称:helloWorld
hello world # 此句的方法内部打印内容

4、后置通知

代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 后置通知
     *
     * @param jp 切点
     * @param result 返回值
     */
    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint jp, Object result) {
        System.out.println("后置通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
        // 获取返回值
        System.out.println("返回值:" + result);
    }

}
控制台打印内容
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
hello world # 此句的方法内部打印内容
后置通知
方法名称:helloWorld
返回值:Hello World!

5、环绕通知

代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param proceedingJoinPoint 连接点
     * @return 连接点方法的返回值
     * @throws Throwable 可能抛出的异常
     */
    @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知 - 前置逻辑");
        // 获取方法名称
        String methodName = proceedingJoinPoint.getSignature().getName();
        System.out.println("方法名称:" + methodName);

        // 执行连接点方法
        Object result = proceedingJoinPoint.proceed();

        System.out.println("环绕通知 - 后置逻辑");
        // 可以对返回值进行处理或修改
        System.out.println("返回值:" + result);

        return result;
    }

}
控制台打印内容
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
环绕通知 - 前置逻辑
方法名称:helloWorld
hello world # 此句的方法内部打印内容
环绕通知 - 后置逻辑
返回值:Hello World!

6、最终通知

代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 最终通知
     *
     * @param jp 切点
     */
    @After("pointcut()")
    public void afterAdvice(JoinPoint jp) {
        System.out.println("最终通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
    }

}
控制台打印内容
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
hello world # 此句的方法内部打印内容
最终通知
方法名称:helloWorld

7、异常通知

代码实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 切点:匹配"com.example.demo.controller"包中所有类的所有方法。
     */
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 异常通知
     *
     * @param jp        切点
     * @param exception 异常对象
     */
    @AfterThrowing(pointcut = "pointcut()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint jp, Exception exception) {
        System.out.println("异常通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
        // 获取异常信息
        System.out.println("异常信息:" + exception.getMessage());
    }

}
修改 HelloController 制造异常
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zibo
 * @date 2023/5/15 12:55
 * @slogan 真正的大师永远怀着一颗学徒的心。——易大师
 */
@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/world")
    public String helloWorld() {
        System.out.println("hello world");
        int num = 10 / 0;
        return "Hello World!";
    }

}
控制台打印内容
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
hello world # 此句的方法内部打印内容
异常通知
方法名称:helloWorld
异常信息:/ by zero

四、切点表达式

1、概述

概述

切入点指示符用来指示切入点表达式目的,在 Spring AOP 中目前只有执行方法这一个连接点,Spring AOP 支持的 AspectJ 切入点指示符,切入点表达式可以使用 &&、||、!来组合切入点表达式,还可以使用类型匹配的通配符来进行匹配。

通配符

类型匹配通配符

说明

*

表示匹配任何数量字符。示例:java.*.String,表示匹配 java 包下的任何"一级子包"下的 String 类型; 如匹配 java.lang.String,但不匹配java.lang.ss.String

表示任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。示例:java…* ,表示匹配java包及任何子包下的任何类型; 如匹配java.lang.String、java.lang.annotation.Annotation

+

仅能作为后缀放在类型模式后边,匹配指定类型的子类型;

2、execution

简介

execution 切点表达式用于定义切点的匹配规则,根据方法的修饰符、返回类型、方法名、参数类型和异常类型等来进行匹配。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
解释
  • modifiers-pattern:方法的修饰符,如 publicprivate 等。可选。
  • ret-type-pattern:方法的返回类型。使用 * 表示任意返回类型,使用完全限定的类名表示具体的返回类型。必选。
  • declaring-type-pattern:方法所在的类或接口。使用完全限定的类名表示具体的类或接口。可选。
  • name-pattern:方法名,使用 * 表示任意方法名。必选。
  • param-pattern:方法的参数类型。使用 * 表示任意参数类型,使用完全限定的类名表示具体的参数类型。可选。
  • throws-pattern:方法抛出的异常类型。使用完全限定的类名表示具体的异常类型。可选。
示例

匹配 public 方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(public * *(..))

匹配名称以 set 开头的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(* set*(..))

匹配指定类的所有方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(* com.example.demo.service.UserService.*(..))

匹配指定包及其子包下的类或接口的所有方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(* com.example.demo.service..*(..))

匹配带有特定注解的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(@com.example.demo.annotation.Loggable * *(..))

匹配返回类型为指定类型的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(java.util.List<com.example.demo.model.User> com.example.demo.service.UserService.*(..))

这些示例展示了不同的 execution 切点表达式的用法,你可以根据具体的需求和要匹配的方法特征来定义切点表达式。

3、within

简介

within 切点表达式用于定义切点的作用范围,根据类型(类或接口)来匹配其中的方法执行。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
within(type-pattern)
示例

匹配指定类中的所有方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
within(com.example.demo.service.UserService)

匹配指定包及其子包下的所有类或接口的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
within(com.example.demo.service..*)

匹配指定包中的所有类或接口的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
within(com.example.demo.service.*)

匹配指定包及其子包下的所有类的所有方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
within(com.example.demo..*)

这些示例展示了使用 within 切点表达式的一些常见用法,你可以根据具体的需求和要匹配的类型来定义切点表达式。

注意

需要注意的是,within 切点表达式只能匹配到类型级别,无法直接匹配到具体的方法。

4、this

简介

this 切点表达式用于匹配当前代理对象所实现的接口类型,并选择这些接口中定义的方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this(type)
示例

这个示例表示匹配当前代理对象所实现的 com.example.demo.service.UserService 接口中定义的所有方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this(com.example.demo.service.UserService)
注意

需要注意的是,this 切点表达式只能匹配到当前代理对象实现的接口方法,并不包括其实现类或其他接口的方法。

5、target

简介

target 切点表达式用于匹配目标对象的类型,并选择这些类型中定义的方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
target(type)
示例

这个示例表示匹配目标对象的类型为 com.example.demo.service.UserService,即选择目标对象为该类型的所有方法作为切点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
target(com.example.demo.service.UserService)
注意

需要注意的是,target 切点表达式匹配的是目标对象的类型,而不是当前代理对象的类型。这意味着它会选择目标对象的方法,而不考虑当前代理对象的实现类或其他接口的方法。

6、args

简介

args 切点表达式用于匹配方法的参数类型,并选择具有匹配参数类型的方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
args(type-pattern)
示例

匹配带有一个整型参数的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
args(int)

匹配带有任意参数类型的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
args(*)

匹配带有一个字符串参数的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
args(java.lang.String)

匹配带有两个整型参数的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
args(int, int)
注意

需要注意的是,args 切点表达式仅匹配参数类型,而不考虑参数名称。它只选择具有匹配参数类型的方法,而不限制参数的个数或顺序。

7、bean

简介

bean 切点表达式用于匹配 Spring 容器中的 Bean 名称,并选择具有匹配名称的 Bean 的方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bean(beanNamePattern)
示例

匹配名称为 “userService” 的 Bean:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bean(userService)

匹配名称以 “service” 结尾的 Bean:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bean(*Service)

匹配名称以 “service” 开头并且包含 “impl” 的 Bean:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bean(service*impl)
注意

需要注意的是,bean 切点表达式匹配的是 Spring 容器中的 Bean 名称,而不是具体的类名或接口名。

8、@within

简介

@within 切点表达式用于匹配被特定注解标注的类及其子类中定义的方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@within(annotation-type)
示例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@within(org.springframework.stereotype.Service)

这个示例表示匹配被 @Service 注解标注的类及其子类中定义的所有方法作为切点。

具体代码示例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.example.demo.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * 切面
 */
@Aspect
@Component
public class DemoAspect {

    /**
     * 异常通知
     *
     * @param jp 切点
     */
    @Before("@within(service)")
    public void beforeAdvice(JoinPoint jp, Service service) {
        System.out.println("前置通知");
        // 获取方法名称
        String methodName = jp.getSignature().getName();
        System.out.println("方法名称:" + methodName);
        // 获取注解的值
        String value = service.value();
        System.out.println("注解的值:" + value);
    }

}

9、@target

简介

@target 切点表达式用于匹配目标对象所属的类上标注的注解类型,并选择这些类中定义的方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@target(annotation-type)
示例

这个示例表示匹配目标对象所属的类上标注了 @Service 注解的所有方法作为切点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@target(org.springframework.stereotype.Service)
注意

需要注意的是,@target 切点表达式匹配的是目标对象所属的类上的注解,而不是当前代理对象所属的类上的注解。它会选择目标对象所属的类中定义的方法,而不考虑当前代理对象的实现类或其他接口的方法。

10、@annotation

简介

@annotation 切点表达式用于匹配被特定注解标注的方法,并选择这些方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@annotation(annotation-type)
示例

这个示例表示匹配被 @RequestMapping 注解标注的方法作为切点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@annotation(org.springframework.web.bind.annotation.RequestMapping)
注意

需要注意的是,@annotation 切点表达式匹配的是方法上的注解,而不是类级别的注解。它会选择被注解标注的方法,而不包括其他方法或类级别的注解。

11、@args

简介

@args 切点表达式用于匹配方法参数上具有特定注解的方法,并选择这些方法作为切点。

语法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@args(annotation-type)
示例

这个示例表示匹配方法参数上具有 @PathVariable 注解的方法作为切点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@args(org.springframework.web.bind.annotation.PathVariable)
注意

需要注意的是,@args 切点表达式匹配的是方法参数上的注解,而不是方法本身或类级别的注解。它会选择具有特定注解的方法,而不包括其他方法或类级别的注解。

五、切点表达式组合

1、概述

切点表达式的组合可以使用逻辑运算符 &&(与)、||(或)、!(非)来组合多个切点表达式。括号可以用于明确定义优先级和逻辑关系。

2、示例

使用逻辑运算符 &&(与):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(public * com.example.service.*Service.*(..)) && @within(org.springframework.stereotype.Service)

该示例表示匹配包名为 com.example.service 的类中,被 @Service 注解标注的方法。

使用逻辑运算符 ||(或):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
execution(public * com.example.controller.*Controller.*(..)) || execution(public * com.example.service.*Service.*(..))

该示例表示匹配包名为 com.example.controller 的类中的方法或者包名为 com.example.service 的类中的方法。

使用逻辑运算符 !(非):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
!execution(public void com.example.controller.AdminController.logout())

该示例表示匹配除了 com.example.controller.AdminController 类中的 logout 方法之外的所有方法。

使用括号进行优先级和逻辑关系的定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(execution(public * com.example.service.*Service.*(..)) && @within(org.springframework.stereotype.Service)) || execution(public * com.example.controller.*Controller.*(..))

该示例表示匹配被 @Service 注解标注的 com.example.service 包中的类的方法,以及匹配 com.example.controller 包中的类的方法。

六、注意事项

  1. AOP代理:
    • Spring AOP 使用代理来实现切面的织入。默认情况下,Spring AOP 使用基于代理的 AOP,其中代理对象包装了目标对象,并在方法调用时执行切面逻辑。代理可以通过 JDK 动态代理或 CGLIB 生成。
    • 注意选择正确的代理模式,例如,当目标对象实现接口时,使用 JDK 动态代理,否则使用 CGLIB 代理。
  2. 切面优先级:
    • 多个切面可以应用于同一个方法或类,因此需要了解切面的优先级。可以通过实现 Ordered 接口或使用 @Order 注解来指定切面的执行顺序。
  3. 切点匹配性能:
    • 切点表达式的复杂度会影响性能。过于复杂的切点表达式可能导致切面执行的性能下降。尽量使用简单且高效的切点表达式,避免不必要的复杂性。
  4. 环绕通知的注意事项:
    • 环绕通知是最强大的通知类型,可以完全控制目标方法的执行。但在使用环绕通知时,需要确保调用 ProceedingJoinPoint 对象的 proceed() 方法,以继续执行目标方法。否则,目标方法将被阻止执行。
  5. 异常处理:
    • 当目标方法抛出异常时,可以使用异常通知来捕获和处理异常。异常通知可以帮助你在方法发生异常时执行额外的逻辑。
  6. Spring AOP 的限制:
    • Spring AOP 只能应用于 Spring 容器管理的 bean 上。它无法拦截自调用的方法、静态方法或无法通过 Spring 容器管理的对象。
  7. Proxy vs. AspectJ:
    • Spring AOP 提供了一种简化的方式来实现切面编程,但其功能相对有限。如果需要更高级的切面功能,例如在构造器、私有方法上织入切面,或者更细粒度的控制,可以考虑使用 AspectJ,它提供了更丰富和灵活的切面编程功能。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
spring aop理解及使用:我想这回应该可以说清楚了吧
当我们剥开aop的外衣的时候,其实他的核心设计思想就是代理模式;spring中大量用到了代理模式;如果你不太了解代理模式,其实也不影响你对aop的使用;这里我举个生活中的例子,带你了解一下什么代理模式;当你在美团、饿了么点餐的时候,其实就是一个典型的代理模式,美团(代理对象)代理了餐馆(目标对象)将美食(方法)卖给你,同时对你的消费进行了增强(帮你配送、送你优惠券等);帮你配送、送你赠品并不是餐厅做的;而是美团(代理对象)做的;但是这一切并没有影响到你就餐、也没有影响餐厅对商品的销售;aop同样也使用的这个方式,在不影响目标对象的前提下对他的功能进行增强。
一行Java
2022/04/06
4870
spring aop理解及使用:我想这回应该可以说清楚了吧
Spring高手之路19——Spring AOP注解指南
在现代软件开发中,面向切面编程(AOP)是一种强大的编程范式,允许开发者跨越应用程序的多个部分定义横切关注点(如日志记录、事务管理等)。本文将介绍如何在Spring框架中通过AspectJ注解以及对应的XML配置来实现AOP,在不改变主业务逻辑的情况下增强应用程序的功能。
砖业洋__
2024/06/16
5750
Spring高手之路19——Spring AOP注解指南
Spring学习总结(三)——Spring实现AOP的多种方式
AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术。AOP是OOP的补充,是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP可以分为静态织入与动态织入,静态织入即在编译前将需织入内容写入目标模块中,这样成本非常高。动态织入则不需要改变目标模块。Spring框架实现了AOP,使用注解配置完成AOP比使用XML配置要更加方便与直观。上一篇随笔中已经详细讲了代理模式。
张果
2022/05/09
4750
Spring学习总结(三)——Spring实现AOP的多种方式
Spring AOP技术原理及利用自定义注解验证数据正确性
1. 切面(Aspect):一个切面由多个通知(Advice)组成,代表了交叉业务逻辑的模块。通知包含了要在目标方法执行前后执行的代码。
用户7353950
2024/04/30
3420
Spring AOP技术原理及利用自定义注解验证数据正确性
Java架构笔记——Spring AOP的实现方式
AOP常用的实现方式有两种,一种是采用声明的方式来实现(基于XML),一种是采用注解的方式来实现(基于AspectJ)。
慕容千语
2019/06/11
6190
解锁Spring Boot AOP的魔力:优雅地管理交叉关注点
想象一下,您正在开发一个大型的Spring Boot应用程序,其中包含成百上千个方法。现在,您需要在这些方法中添加相同的日志记录或安全性检查。这时候,AOP(面向切面编程)就派上了用场。本博客将引导您进入Spring Boot AOP的令人着迷的世界,让您了解如何通过AOP提高代码的可维护性和可重用性,同时让开发变得更有趣。
一只牛博
2025/05/30
1330
【Spring进阶】基于注解的面向切面编程(AOP)详解
面向切面编程(AOP)是一种编程范式,它允许开发者将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高代码的模块化和可维护性。在Java中,AOP通常通过使用框架如Spring来实现。
王也518
2024/04/22
1.5K0
Spring AOP简介及相关案例
会洗碗的CV工程师
2023/10/14
2290
Spring AOP简介及相关案例
Java开发框架之Spring AOP知识总结
AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充,它的主要编程对象是切面(aspect), 而切面模块化横切关注点.在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里。
用户1289394
2018/12/29
6060
Java开发框架之Spring AOP知识总结
_Spring AOP简介及相关案例
OK,确实是打印了发送邮件,因此该多切面配置成功,下面接着讲解用另外几种方法实现AOP ,让我们一起学习啪
会洗碗的CV工程师
2023/11/19
1640
_Spring AOP简介及相关案例
轻松上手Spring AOP,掌握切面编程的核心技巧
Spring框架是我们使用比较多的一个框架,而AOP又是Spring的核心特性之一,本篇文章将介绍一下AOP的切点表达式、通知等特性及如何使用Spring AOP。
索码理
2024/04/15
3480
轻松上手Spring AOP,掌握切面编程的核心技巧
Spring(4)——面向切面编程(AOP模块)
Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。 AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 所谓的周边功能,比如性能统计,日志,事务管理等等 周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面 在面向切面编程AO
我没有三颗心脏
2018/04/26
6860
Spring(4)——面向切面编程(AOP模块)
Spring官网阅读(十八)AOP的核心概念
在前面的文章中我们已经对IOC做过详细的介绍了,本文主要介绍AOP,关于其中的源码部分将在专门的源码专题介绍,本文主要涉及的是AOP的基本概念以及如何使用,本文主要涉及到官网中的第5、6两大章 ”
程序员DMZ
2020/07/07
8110
深入浅出Spring AOP:让你的代码更优雅
在现代Java开发中,Spring框架几乎是无处不在的。作为Spring框架的一部分,Spring AOP(面向切面编程)提供了一种强大且灵活的方式来处理横切关注点,比如日志记录、安全检查、事务管理等。如果你还没有完全掌握Spring AOP,那么这篇文章将带你深入了解它的工作原理和应用场景。
AI码师
2024/05/27
1.3K0
深入浅出Spring AOP:让你的代码更优雅
Spring 框架学习(六)面向切面编程 AOP
在软件开发中散布于应用中多处的功能被称为横切关注点(crossing-cutting concern)。通常这也横切关注点一般是与业务逻辑相分离的。而面向切面编程将会解决如何将横切关注点与与业务逻辑分离的问题。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRIEcgxi-1571404087194)(en-resource://database/9500:1)] 横切关注点往往是影响应用多处的功能
求和小熊猫
2020/11/25
3690
Spring 中 AOP 的实现
AOP 称为面向切面编程,在程序开发中,AOP 技术可以在不改变原有方法代码的情况下,把逻辑直接插入到方法上。Spring AOP 的大致原理主要依靠的是动态代理,你以为操作的这个类,实际上操作的是它的一个代理类。动态代理用到两种机制,一直是基于 JDK 的动态代理,另一种是基于 CGLib 的动态代理。网上有很多介绍这种原理的文章,深入了解可以参考这些文章。
水货程序员
2018/11/13
5430
AOP切面编程
如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。
用户3467126
2019/08/12
6570
Spring 框架学习(八)——AOP 的认识与使用
通知类型就是想要加的代码(校验、日志等) 是在对象方法的前面还是后面执行的类型,这就是通知类型。
RAIN7
2022/07/09
5260
Spring 框架学习(八)——AOP 的认识与使用
揭秘AOP:切面编程的综合指南
Spring的AOP(面向切面编程)是一种编程范式,它允许开发人员将横切关注点(cross-cutting concerns)从应用程序的主要业务逻辑中分离出来,以便更好地实现代码重用和模块化。横切关注点指的是那些存在于应用程序多个模块中的功能,如日志记录、事务管理、安全性等,它们不属于单个类或对象,而是跨越多个组件的功能。
堆栈哲学
2024/03/23
6950
揭秘AOP:切面编程的综合指南
Spring 详解(二)------- AOP关键概念以及两种实现方式
当我们为系统做参数验证,登录权限验证或者日志操作等,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。但是这样我们仍然必须手动插入这些方法,这样的话模块之间高耦合,不利于后期的维护和功能的扩展,有了 AOP 我们可以将功能抽成一个切面,代码复用好,低耦合。
海向
2019/09/23
6790
Spring 详解(二)------- AOP关键概念以及两种实现方式
相关推荐
spring aop理解及使用:我想这回应该可以说清楚了吧
更多 >
LV.3
这个人很懒,什么都没有留下~
目录
  • Spring AOP 实践指南
  • 一、概述
    • 1、简介
    • 2、官方资料
    • 3、本文档说明
  • 二、基本使用
    • 1、引入依赖
    • 2、定义切面
    • 3、定义切点
    • 4、创建 HelloController
    • 5、启动项目,访问测试
  • 三、通知
    • 1、概述
      • 五种通知
      • 通知的顺序
    • 2、通知方法接受的参数
    • 3、前置通知
      • 代码实现
      • 控制台打印内容
    • 4、后置通知
      • 代码实现
      • 控制台打印内容
    • 5、环绕通知
      • 代码实现
      • 控制台打印内容
    • 6、最终通知
      • 代码实现
      • 控制台打印内容
    • 7、异常通知
      • 代码实现
      • 修改 HelloController 制造异常
      • 控制台打印内容
  • 四、切点表达式
    • 1、概述
      • 概述
      • 通配符
    • 2、execution
      • 简介
      • 语法
      • 解释
      • 示例
    • 3、within
      • 简介
      • 语法
      • 示例
      • 注意
    • 4、this
      • 简介
      • 语法
      • 示例
      • 注意
    • 5、target
      • 简介
      • 语法
      • 示例
      • 注意
    • 6、args
      • 简介
      • 语法
      • 示例
      • 注意
    • 7、bean
      • 简介
      • 语法
      • 示例
      • 注意
    • 8、@within
      • 简介
      • 语法
      • 示例
      • 具体代码示例
    • 9、@target
      • 简介
      • 语法
      • 示例
      • 注意
    • 10、@annotation
      • 简介
      • 语法
      • 注意
    • 11、@args
      • 简介
      • 语法
      • 示例
      • 注意
  • 五、切点表达式组合
    • 1、概述
    • 2、示例
  • 六、注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档