1. 概述
本文将使用Spring的AOP支持实现一个自定义AOP注解。首先会简要介绍AOP概念及其优势,然后逐步实现注解,在过程中深入理解AOP核心机制。最终目标不仅是掌握AOP原理,还能具备独立创建自定义Spring注解的能力。
2. 什么是AOP注解?
AOP即面向切面编程,核心思想是在不修改源代码的情况下为现有功能添加额外行为。关于AOP基础概念(如切入点、通知等)可参考相关文章(本文假设读者已具备基础知识)。
我们即将实现的是注解驱动的AOP。如果你用过Spring的@Transactional
注解,应该对这种模式不陌生:
@Transactional
public void orderGoods(Order order) {
// 一系列需要在事务中执行的数据库操作
}
这种模式的核心优势是非侵入性。通过注解元数据,核心业务逻辑不会被事务代码污染,使代码更易理解、重构和独立测试。
很多开发者将其视为"Spring魔法"而不深究原理,其实实现并不复杂。完成本文步骤后,你就能创建自己的注解来理解和运用AOP。
3. Maven依赖
首先添加Maven依赖。我们使用Spring Boot快速搭建项目:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
关键点:引入spring-boot-starter-aop
会自动拉取实现切面所需的核心库。
4. 创建自定义注解
我们将创建一个用于记录方法执行时间的注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
虽然实现简单,但需要理解两个元注解的作用:
@Target
:指定注解适用范围。这里用ElementType.METHOD
表示仅适用于方法,如果用在其他地方会编译失败。这符合我们的需求——记录方法执行时间。@Retention
:控制注解生命周期。默认情况下注解在运行时不可见,而Spring AOP需要运行时访问,因此必须设置为RetentionPolicy.RUNTIME
。
5. 创建切面
现在创建切面类——封装横切关注点(此处是方法执行时间日志)的模块:
@Aspect
@Component
public class ExampleAspect {
}
注意:
@Aspect
标记该类为切面@Component
使其成为Spring Bean(必须,否则无法被扫描到)
这个类将实现我们自定义注解要注入的逻辑。
6. 创建切入点和通知
在切面类中添加带注解的方法:
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
return joinPoint.proceed();
}
这段代码虽未改变行为,但包含多个关键概念:
@Around
:环绕通知,在方法执行前后添加逻辑。其他类型通知(如@Before
/@After
)本文不展开。- 切入点表达式:
"@annotation(LogExecutionTime)"
表示"应用于所有带@LogExecutionTime
注解的方法"。 ProceedingJoinPoint
参数:代表被注解的方法执行点。joinPoint.proceed()
:调用原始方法。当前实现仅执行原始方法,无额外逻辑。
执行流程:当注解方法被调用时,先触发通知逻辑,由通知决定后续操作(此处直接调用原始方法)。
7. 记录执行时间
现在为通知添加计时逻辑:
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
实现逻辑:
- 记录开始时间
- 执行原始方法
- 计算耗时并打印
- 返回原始方法结果
通过joinPoint
可获取方法签名(如void com.example.Service.serve()
),还能访问方法参数等信息。
测试用例(必须是Spring Bean):
@LogExecutionTime
public void serve() throws InterruptedException {
Thread.sleep(2000);
}
执行后控制台输出示例:
void com.example.Service.serve() executed in 2030ms
8. 总结
本文通过Spring Boot AOP实现了自定义注解,可应用于Spring Bean在运行时注入额外行为。完整代码可在GitHub获取(Maven项目可直接运行)。
关键收获:
✅ 掌握自定义AOP注解完整流程
✅ 理解切面、切入点、通知等核心概念
✅ 学会非侵入式增强现有功能
后续可尝试扩展:添加注解属性控制日志级别、结合SpEL动态配置等。