文章目录
- 一、AOP是什么?
-
- [1. AOP的核心概念](#1. AOP的核心概念)
-
- 切面(Aspect)
- [连接点(Join Point)](#连接点(Join Point))
- 切点(Pointcut)
- 通知(Advice)
- [目标对象(Target Object)](#目标对象(Target Object))
- [AOP代理(AOP Proxy)](#AOP代理(AOP Proxy))
- [2. AOP的优势](#2. AOP的优势)
- [3. AOP的实现方式](#3. AOP的实现方式)
- [4. Spring AOP概述](#4. Spring AOP概述)
- [二、Spring Boot AOP 基础](#二、Spring Boot AOP 基础)
- 三、实战案例
-
- [1. 日志记录](#1. 日志记录)
- [2. 事务管理](#2. 事务管理)
一、AOP是什么?
面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,通过分离横切关注点来提高模块化程度。AOP 允许开发者定义可以在代码执行的某些点(切点)执行的额外行为(切面),从而避免代码的重复和冗余。
1. AOP的核心概念
切面(Aspect)
切面是 AOP 的核心概念之一,代表一个横切关注点的模块化。切面可以包含多个通知(Advice),这些通知定义了切面在特定切点(Join Point)上的行为。
连接点(Join Point)
连接点是在程序执行的过程中可以插入切面的具体点。Spring AOP 支持的方法执行连接点,意味着切面可以在方法调用前后或方法抛出异常时执行。
切点(Pointcut)
切点是一个表达式,定义了哪些连接点会被切面切入。切点表达式通常使用类名、方法名、参数等信息来指定需要织入的连接点。
通知(Advice)
通知是切面在切点处所执行的代码。通知有多种类型,包括前置通知(Before)、后置通知(After)、返回通知(After Returning)、异常通知(After Throwing)和环绕通知(Around)。
目标对象(Target Object)
目标对象是被一个或多个切面切入的对象。Spring AOP 使用 JDK 动态代理或 CGLIB 代理来为目标对象创建代理对象。
AOP代理(AOP Proxy)
AOP 代理是由 AOP 框架生成的对象,用于实现切面契约。Spring AOP 使用 JDK 动态代理来代理实现接口的类,使用 CGLIB 来代理没有实现接口的类。
2. AOP的优势
模块化
通过 AOP,可以将日志记录、事务管理、安全性等横切关注点与业务逻辑分离,提升代码的模块化程度,使得每个模块职责单一、清晰。
代码复用
AOP 使得相同的横切关注点代码可以在多个模块中复用,减少代码重复,提升代码的可维护性。
解耦
业务逻辑代码与横切关注点代码解耦,业务逻辑代码更加简洁、清晰,横切关注点的变化不会影响业务逻辑代码。
3. AOP的实现方式
编译时织入
在编译代码时将切面织入目标代码,使用编译器提供的机制将横切关注点的代码编译到目标类中。这种方式的代表是 AspectJ。
类加载时织入
在类加载时通过类加载器将切面织入目标类。这种方式需要定制类加载器,较为复杂。
运行时织入
在应用程序运行时通过动态代理技术将切面织入目标对象。这是 Spring AOP 采用的方式,通过 JDK 动态代理或 CGLIB 实现。
4. Spring AOP概述
Spring AOP 是 Spring 框架的一部分,提供了简单易用的 AOP 功能。Spring AOP 使用动态代理实现运行时织入,并支持基于注解和基于 XML 的配置方式。Spring AOP 主要用于处理方法级别的横切关注点,适用于大多数企业应用场景。
二、Spring Boot AOP 基础
1. 引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建切面
java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBeforeMethod() {
System.out.println("A method is about to be executed.");
}
}
@Aspect
注解表明该类是一个切面类,@Component
注解将其注册为 Spring 容器的一个 Bean。@Before
注解用于定义前置通知,在目标方法执行前执行。
3. 定义切点
切点定义了切面织入的具体位置,可以通过 @Pointcut
注解来定义。
java
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Pointcut
注解定义了一个名为 serviceLayer
的切点,该切点匹配 com.example.service
包中的所有方法执行。
4. 应用通知
通知是在切点处执行的代码,可以在目标方法执行前后或抛出异常时执行。Spring AOP 提供了多种类型的通知,如前置通知、后置通知、返回通知、异常通知和环绕通知。
后置通知
后置通知在目标方法执行后执行:
java
import org.aspectj.lang.annotation.After;
@After("execution(* com.example.service.*.*(..))")
public void logAfterMethod() {
System.out.println("A method has just been executed.");
}
环绕通知
环绕通知可以在目标方法执行前后执行,并且可以控制目标方法的执行:
java
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Around("execution(* com.example.service.*.*(..))")
public Object logAroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Method execution start.");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("Method execution end.");
return result;
}
@Around
注解定义了一个环绕通知,ProceedingJoinPoint
对象用于执行目标方法。环绕通知在目标方法执行前后打印日志信息。
三、实战案例
1. 日志记录
创建一个记录日志的切面
java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.controller.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Executing: " + joinPoint.getSignature().getName());
}
@AfterReturning(pointcut = "execution(* com.example.controller.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("Executed: " + joinPoint.getSignature().getName() + ", Returned: " + result);
}
}
@Before
注解定义了前置通知,记录方法执行前的信息。@AfterReturning
注解定义了后置返回通知,记录方法执行后的返回结果。logBefore
方法在com.example.controller
包下的所有方法执行之前打印日志,记录即将执行的方法名称。logAfterReturning
方法在com.example.controller
包下的所有方法执行之后打印日志,记录方法名称和返回结果。
2. 事务管理
创建一个事务管理的切面
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TransactionAspect {
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Transaction start.");
Object result = joinPoint.proceed();
System.out.println("Transaction end.");
return result;
}
}
@Around
注解定义了环绕通知,用于在方法执行前后执行自定义逻辑。manageTransaction
方法在带有@Transactional
注解的方法执行前后打印事务开始和结束的日志。ProceedingJoinPoint
对象用于执行目标方法,并在目标方法执行前后插入自定义逻辑。