一、AOP的概念
1.原理
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程思想,用于将通用的横切关注点(如日志记录、事务管理、安全性等)与核心业务逻辑分离。通过 AOP,可以在不修改业务逻辑代码的情况下动态地向程序中添加功能。
2.1 切面(Aspect):
-
包含切点和通知的模块化关注点,例如日志、事务。
2.2 通知(Advice):
-
定义切面在目标方法执行时所采取的操作:
-
@Before:在方法执行前执行。
-
@AfterReturning:在方法返回后执行。
-
@AfterThrowing:在方法抛出异常时执行。
-
@After:无论方法是否成功执行,都会执行。
-
@Around:环绕通知,可以控制方法执行前后行为。
-
2.3 切点(Pointcut):
-
定义拦截的范围和条件,比如某些包下的类或特定注解。
2.4 目标对象(Target):
-
被 AOP 代理的对象。
2.5 织入(Weaving):
-
将切面逻辑应用到目标对象上的过程。
2.6 代理对象(Proxy):
-
目标对象的增强版本,通过代理调用目标方法。
二、使用步骤
2.1例如:以向数据库中插入或更新字段时需要插入修改时间为例:
2.1.1 定义横切关注点:注解
定义一个注解(@AutoFill
),用来标记需要切面增强的方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
OperationType value(); // 数据库操作类型,例如 INSERT、UPDATE
}
2.1.2 定义切面类
切面类中定义切点和通知逻辑。
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
// 定义切点,拦截带有 @AutoFill 注解的方法
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut() {
//关于@PointCut的解释:(* com.sky.mapper.*.*(..))
// 1. execution是切点表达式,定义拦截范围
// 1.1 第一个*(在com之前的*):
// 表示方法的返回值,*表示拦截所有返回值的类型,比如void、string、int等
// 1.2 com.sky.mapper.*.*(..):
// 具体意义为:com.sky.mapper包下的所有类(注意不是所有子类,如果想包括子包,可以用..)的所有方法,
// (..)表示任意参数个数和类型
//
// 2. @annotation(com.sky.annotation.AutoFill):
// 2.1 @annotation:
// 表示方法上需要带有指定注解
// 2.2 (com.sky.annotation.AutoFill)中的内容:
// 是限定注解的全称限定名
}
// 定义通知,目标方法执行前调用
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
/**
* JoinPoint JoinPoint 是 Spring AOP 提供的接口,用于在切面中获取被拦截方法的上下文信息,
* 包括方法签名(MethodSignature)、方法参数等。
* 因此,JoinPoint 是 Spring AOP 的特定功能,不能在非 Spring 框架中直接使用。
*/
log.info("公共字段进行补充...");
// 获取方法上的注解
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
OperationType operationType = autoFill.value();
// 获取方法的参数,执行反射赋值逻辑
Object[] args = joinPoint.getArgs();
Object entity = args[0]; // 假设实体对象在第一个参数
//... coding剩余逻辑
}
}
2.1.3. 使用 AOP 注解
将切面增强逻辑应用到 Mapper 方法。
public interface EmployeeMapper {
@AutoFill(OperationType.INSERT)
void insert(Employee employee);
@AutoFill(OperationType.UPDATE)
void update(Employee employee);
}
2.1.4. Spring 容器配置
Spring Boot 项目中默认开启 AOP 支持,只需引入依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.2 再例如:使用AOP监控某一方法性能:
注意在这里没有自定义注解,是因为希望该功能作用在每一个方法上,所以不需要自定义注解来指定哪个方法需要执行该功能。
2.2.1. 定义切面类
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class PerformanceMonitorAspect {
// 切点:拦截 com.example.service 包下的所有方法
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法名称
String methodName = joinPoint.getSignature().getName();
// 记录开始时间
long startTime = System.currentTimeMillis();
// 执行目标方法
Object result;
try {
result = joinPoint.proceed();
} finally {
// 记录结束时间
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
// 打印日志
log.info("Method [{}] executed in {} ms", methodName, duration);
}
return result;
}
}
2.2.2. 使用AOP注解
定义一个服务类,用于测试切面功能。
import org.springframework.stereotype.Service;
@Service
public class SampleService {
public String performTask() {
// 模拟业务逻辑
try {
Thread.sleep(500); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Task Completed";
}
public void anotherTask() {
// 模拟另一个任务
try {
Thread.sleep(300); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.2.3. 容器配置
确保项目中已引入 Spring AOP 相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
三、其它细节
3.1 Signature 和 MethodSignature:
如果需要访问方法的参数、返回值类型、注解等更详细的信息,必须使用 MethodSignature
3.1.1 Signature:
Signature
是一个通用接口,表示连接点(JoinPoint)签名的基本信息,适用于方法、构造器等。
常用方法:
-
String getName()
返回方法或构造器的名称。 -
String getDeclaringTypeName()
返回声明该签名的类或接口的全限定名。 -
Class<?> getDeclaringType()
返回声明该签名的类或接口的Class
对象。
Signature signature = joinPoint.getSignature();
System.out.println("Name: " + signature.getName()); // 方法名
System.out.println("Declaring Type: " + signature.getDeclaringTypeName()); // 全类名
3.1.2 MethodSignature
MethodSignature是Signature 的子接口,提供了专门用于描述方法的扩展信息。
常用方法:
-
继承自
Signature
的方法。 -
Method getMethod()
返回被拦截的方法对象(java.lang.reflect.Method
)。 -
Class<?>[] getParameterTypes()
返回方法参数的类型数组。 -
Type[] getGenericParameterTypes()
返回方法参数的泛型类型数组。 -
Class<?> getReturnType()
返回方法的返回值类型。 -
Type getGenericReturnType()
返回方法的泛型返回值类型。 -
String[] getParameterNames()
返回方法参数的名称数组(需要 AOP 配置正确支持参数名)。
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
System.out.println("Method: " + methodSignature.getMethod()); // 方法对象
System.out.println("Return Type: " + methodSignature.getReturnType()); // 返回值类型
System.out.println("Parameter Names: " + Arrays.toString(methodSignature.getParameterNames())); // 参数名