Spring AOP详解

Spring AOP是Spring框架中的一个模块,它允许开发人员使用面向切面编程(AOP)的思想来解耦系统的不同层次。

Spring AOP的核心概念是切面(aspect)、连接点(join point)、通知(advice)、切点(pointcut)和引入(introduction)。

  • 切面(aspect):切面是一个类, 它包含了通知(advice)和切点(pointcut)。
  • 连接点(join point):在AOP中,连接点是程序执行的某个时间点,例如方法调用、异常抛出、对象实例化等。
  • 通知(advice):在连接点上执行的代码片段,它们包括before、after、afterReturning、afterThrowing和around五种类型。
  • 切点(pointcut):在程序执行时选择哪些连接点执行通知的表达式,例如execution(* com.example.demo.*(..))表示匹配com.example.demo包下的任意方法。
  • 引入(introduction):在不修改现有类代码的情况下,向现有类添加新方法或属性。

Spring AOP底层使用动态代理和字节码生成来实现。切面由通知和切点组成,连接点是程序执行的某个时间点,切点根据表达式匹配连接点,通知是在连接点上执行的代码片段,在方法调用前或调用后执行某些操作。

Spring AOP的优点是可以通过配置实现解耦,不用修改经常变动的业务逻辑代码,而且可以重用通知,降低代码冗余程度。

以下是一个使用Spring Boot AOP实现日志记录的例子:

1.创建一个Spring Boot项目并添加如下依赖:

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

2.定义一个"UserService"接口和一个实现类"UserServiceImpl",实现类中包含一个方法"getUserById":

java 复制代码
public interface UserService {
    User getUserById(long id);
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public User getUserById(long id) {
        System.out.println("调用getUserById方法,id=" + id);
        return new User(id, "张三");
    }
}
 

3.定义一个切面"LogAspect",

使用注解@Aspect标识切面,

使用@Pointcut定义切点,

使用

@Before、

@After、

@AfterReturning、

@AfterThrowing、

@Around注解定义通知:

java 复制代码
@Aspect
@Component
public class LogAspect {
    @Pointcut("execution(* com.example.demo.service.UserService.*(..))")
    public void userServicePointcut() {}
    
    @Before("userServicePointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("调用" + joinPoint.getSignature().getName() + "方法");
    }
    
    @AfterReturning(pointcut = "userServicePointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("调用" + joinPoint.getSignature().getName() + "方法完成,返回值=" + result);
    }
    
    @AfterThrowing(pointcut = "userServicePointcut()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        System.out.println("调用" + joinPoint.getSignature().getName() + "方法出现异常,异常信息=" + exception.getMessage());
    }
}
 

在上面的切面中,我们使用@Pointcut注解定义切点,使用@Before、@AfterReturning、@AfterThrowing等注解定义通知,在通知中使用JoinPoint获取方法签名、参数等信息,输出日志。

4.运行应用程序并测试:

java 复制代码
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable long id) {
        return userService.getUserById(id);
    }
}
 

在控制台中可以看到如下输出:

routeros 复制代码
调用getUserById方法,id=1
调用getUserById方法完成,返回值=User [id=1, name=张三]
 

匹配方法

Pointcut是基于表达式的描述符,用于指定应该在哪些方法上执行通知。Pointcut表达式可以使用AspectJ的语法,也可以使用Spring AOP的语法。

以下是常用的Pointcut表达式:

  1. execution:使用该表达式匹配方法执行的切点。
  • execution(public * com.example.demo.service.UserService.*(..)):匹配com.example.demo.service.UserService类中所有public修饰符的方法。

  • execution(* com.example.demo.service..(..)):匹配com.example.demo.service包下所有类的任意方法。

  • execution(* com.example.demo.service...(..)):匹配com.example.demo.service包以及其子包下所有类的任意方法。

  1. within:使用该表达式匹配指定类型的切点,包括该类型的子类型。
  • within(com.example.demo.service.*):匹配所有com.example.demo.service包中的类型。

  • within(com.example.demo.service..*):匹配com.example.demo.service包以及其子包中的所有类型。

  1. annotation:使用该表达式匹配带有指定注解的切点。
  • @annotation(org.springframework.web.bind.annotation.RequestMapping):匹配带有@RequestMapping注解的方法。

  • @within(org.springframework.stereotype.Service):匹配带有@Service注解的类中的所有方法。

  1. args:使用该表达式匹配带有指定参数类型的切点。
  • args(java.lang.String):匹配一个参数类型为String的方法。

  • args(java.lang.String, ...):匹配至少一个参数类型为String的方法。

  1. bean:使用该表达式匹配指定名称或类型的bean的切点。
  • bean(userService):匹配名称为userService的bean。

  • bean(userService*):匹配名称以userService开头的bean。

除了以上的表达式外,还有很多其他的表达式,可以根据具体的需求选择使用。需要注意的是,在使用Pointcut表达式时,需要注意匹配的粒度,匹配过于精确会导致无法匹配到需要的切点,匹配过于宽泛则会匹配到不需要的切点。

相关推荐
木子欢儿3 分钟前
在 Debian 12 上安装 Java 21
java·运维·开发语言·debian
一二小选手6 分钟前
【高级编程】XML DOM4J解析XML文件(含案例)
xml·java
终末圆7 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
就这个java爽!7 分钟前
超详细的XML介绍【附带dom4j操作XML】
xml·java·开发语言·数据库·青少年编程·eclipse
Damon小智9 分钟前
C#进阶-基于雪花算法的订单号设计与实现
开发语言·算法·c#·雪花算法·订单号
kunkun1019 分钟前
Mybatis的XML实现方法
xml·java·mybatis
_.Switch13 分钟前
Python Web 架构设计与性能优化
开发语言·前端·数据库·后端·python·架构·log4j
libai16 分钟前
STM32 USB HOST CDC 驱动CH340
java·前端·stm32
IT学长编程16 分钟前
计算机毕业设计 数字化农家乐管理平台的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·毕业论文·计算机毕业设计选题·计算机毕业设计开题报告·农家乐管理平台
Q1860000000017 分钟前
用java语言写一个表的查询操作
java·开发语言·oracle