Spring Boot2.x教程:(九)AOP基本概念与示例
AOP基本概念与示例
- 1、概述
- 2、概念
- 2.1、什么是连接点
- 2.2、什么是切点
- 2.3、切点的作用
- 2.4、切点不能单独执行
- 2.5、什么是通知
- 2.6、通知的作用
- 2.7、切点与通知的关系
- 2.8、连接点与切点的关系
- 2.9、什么是切面
- 2.10、切面的组成
- 2.11、切面的作用
- 2.12 织入
- 3、示例演示
- 3.1、程序的执行流程
- 4、总结
大家好,我是欧阳方超,可以扫描下方二维码关注我的公众号“欧阳方超”,后续内容将在公众号首发。
1、概述
Aspect-Oriented Programming (AOP) 是一种编程范式,旨在将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来。Spring Boot 提供了强大的 AOP 支持,使开发者能够轻松实现这些功能。本文将探讨 AOP 的基本机制及其在 Spring Boot 中的应用。
2、概念
在面向切面编程中(AOP),连接点(Joinpoint)、切点(Pointcut)、通知、切面、织入是两个关键概念,它们共同构成了AOP的核心机制。
2.1、什么是连接点
连接点(Joinpoint)是程序执行中的一个具体时刻,比如方法调用前、方法调用后、方法抛出异常时等。在Spring AOP中,连接点主要指的是方法的执行。想象你开车经过一条高速公路,这条高速公路有多个出口,每个出口都可以视为一个连接点,虽然所有这些出口都是可以选择的,但你只会选择一个特定的出口来驶离高速,这就像在程序中,所有满足条件的时机都是连接点,但你只会在特定的连接点上应用通知。
2.2、什么是切点
切点(Pointcut)是AOP中用于定义在哪些连接点(Joinpoint)上应用通知的机制。它通过切点表达式来匹配特定的方法或连接点。举例说明换一下,
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
如上定义了一个切点,它可以匹配所有被@GetMapping注解修饰的方法。
2.3、切点的作用
切点用户指定哪些方法或连接点需要被增强,通过不同的表达式,开发者可以灵活选择需要拦截的方法。
2.4、切点不能单独执行
注意,切点是不能单独执行的,这与它的设计目的有关,切点只是用于匹配连接点的工具,它的设计目的是为了与通知结合使用。只有当切点匹配到某个连接点时,相应的通知才会被执行,从而实现具体任务。
2.5、什么是通知
通知(Advice)是与切点关联的实际执行逻辑,它定义了在特定连接点上要执行的操作。AOP有多种类型的通知,包括:
前置通知(@Before注解):在目标方法执行之前执行。
后置通知(@After注解):在目标方法执行之后执行。
环绕通知(@Around注解):在目标方法执行之前和之后都可以执行自定义逻辑。
2.6、通知的作用
增强功能:通知可以用来实现日志记录、性能监控、权限检查等横切关注点。
代码解耦:通过将横切关注点与业务逻辑分离,提高了代码的可维护性和科扩展性。
2.7、切点与通知的关系
切点与通知是相辅相成的:
依赖关系:切点本身并不包括任何执行逻辑,它只是一个匹配规则,没有通知,切点无法触发任何操作。
结合使用:通常情况下,开发者会定义多个切点和相应的通知,以实现灵活和可重用的横切关注点处理。
2.8、连接点与切点的关系
切点是用于定义哪些连接点需要被增强的表达式。它可以看作是从众多连接点中筛选出特定的一部分。例如,如果你只想在某个特定的方法调用前执行一些逻辑,你就会定义各一个切点来匹配那个方法。
2.9、什么是切面
切面是一个模块化的单元,专门用于处理那些与业务逻辑无关但又在多个地方需要的功能,比如日志记录、事务管理、安全控制,通过将这些功能抽象为切面,开发者可以将他们从核心业务逻辑中分离出来,从而提高代码的可维护性和清晰度。
2.10、切面的组成
切面通常由两个主要部分组成:
切点(Pointcut):定义在哪些连接点(如方法调用)上应用通知。切点通过表达式来描述,例如,可以匹配特定包中的所有方法。
通知(Advice):在切点匹配到的连接点上执行的具体代码。
2.11、切面的作用
解耦:通过将横切关注点与业务逻辑分离,切面使得代码更加模块化;
复用:同一切面可以在多个地方被重用,而不需要重写代码;
集中管理:所有横切关注点的处理逻辑集中在切面中,便于统一管理和修改。
2.12 织入
织入(Weaving)是将切面应用到目标对象的过程,可以在编译期、类加载期或运行期进行。在 Spring AOP 中,通常使用动态代理来实现织入。
3、示例演示
只看理论未免显得有些乏味,下面结合示例来体会一下。
在Spring Boot项目中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义一个UserService类:
package com.xxx.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void addUser(String name) {
System.out.println("User " + name + " added.");
}
}
定义切面:
package com.xxx.service.component;
import org.aspectj.lang.JoinPoint;
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 LoggingAspect {
@Pointcut("execution(* com.xxx.service.UserService.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void logBefore(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("A method in UserService is about to be called.");
}
}
然后再定义Controller:
package com.xxx.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/log")
public class LogController {
@Autowired
private UserService userService;
@RequestMapping("/test")
public void test() {
userService.addUser("Peter");
}
}
3.1、程序的执行流程
当请求接口/log/test时,控制器方法内部会调用userService.addUser(name), 此时Spring AOP会检查是否有与该方法相关的切点。在上面的示例中,切面LoggingAspect定义了一个切点(@Pointcut(“execution(* com.xxx.service.UserService.*(…))”)),这个切点匹配了UserService类中的所有方法,因此addUser()方法的切点匹配成功。切点匹配成功后,Spring AOP将执行与该切点关联的前置通知,在这里会执行logBefore()方法,由于传入了JoinPoint参数,可以获取到被调用方法的信息。此时控制台将输出:
A method in UserService is about to be called: addUser
前置通知执行完毕后,控制器方法继续执行,调用UserService.addUser()方法。
注意,在AOP中,切点(Pointcut)并不是绝对必要的,虽然他们通常用于定义在哪些连接点上应用通知,确实可以在没有显式定义切点的方法上直接使用通知,所以上面发的例子中切面类也可以这样写:
package com.xxx.service.component;
import org.aspectj.lang.JoinPoint;
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 LoggingAspect {
@Before("execution(* com.xxx.service.UserService.*(..))")
public void logBefore(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("A method in UserService is about to be called.");
}
}
4、总结
AOP 是一种强大的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性和可扩展性。Spring Boot 提供了便捷的 AOP 支持,使得开发者能够轻松实现日志记录、事务管理等功能。通过理解切点、通知、连接点和织入等基本概念,开发者可以更有效地利用 AOP 来优化应用程序架构。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。