摘要:Spring AOP 的注解实现提供了声明式的切面编程能力,结合自定义注解可轻松实现日志记录、性能监控、事务管理等横切关注点,大幅提升代码复用性和可维护性。
Spring AOP 注解实现详解
Spring AOP 通过注解提供了简洁的面向切面编程实现方式。以下是完整实现步骤:
1. 添加 Maven 依赖
org.springframework
spring-context
5.3.23
org.springframework
spring-aop
5.3.23
org.aspectj
aspectjweaver
1.9.9.1
2. 创建目标服务类
java
@Service
public class UserService {
public void createUser(String username) {
System.out.println("创建用户: " + username);
}
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
}
3. 创建切面类(核心实现)
java
@Aspect
@Component
public class LoggingAspect {
// 定义切入点(匹配所有Service层方法)
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer {}
// 前置通知
@Before("serviceLayer")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature.getName;
System.out.println("[前置通知] 方法执行: " + methodName);
}
// 返回后通知
@AfterReturning(pointcut = "serviceLayer", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("[返回通知] 方法返回: " + result);
}
// 环绕通知(可控制方法执行)
@Around("serviceLayer")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis;
Object result = joinPoint.proceed; // 执行目标方法
long time = System.currentTimeMillis - start;
System.out.println("[环绕通知] 执行耗时: " + time + "ms");
return result;
}
// 异常通知
@AfterThrowing(pointcut = "serviceLayer", throwing = "ex")
public void logException(JoinPoint joinPoint, Exception ex) {
System.err.println("[异常通知] 方法异常: " + ex.getMessage);
}
// 最终通知(类似finally)
@After("serviceLayer")
public void logAfter(JoinPoint joinPoint) {
System.out.println("[最终通知] 方法结束");
}
}
4. 配置类启用AOP
java
@Configuration
@ComponentScan(basePackages = "com.example")
@EnableAspectJAutoProxy // 关键注解:启用AOP代理
public class AppConfig {
}
5. 测试类
java
public class AopTest {
public static void main(String args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.createUser("张三");
userService.deleteUser("李四");
}
}
执行结果示例
[前置通知] 方法执行: createUser
[环绕通知] 执行开始...
创建用户: 张三
[返回通知] 方法返回: null
[最终通知] 方法结束
[环绕通知] 执行耗时: 12ms
[前置通知] 方法执行: deleteUser
[环绕通知] 执行开始...
删除用户: 李四
[返回通知] 方法返回: null
[最终通知] 方法结束
[环绕通知] 执行耗时: 5ms
核心注解说明
注解作用@Aspect声明当前类为切面类@Pointcut定义可重用的切入点表达式@Before前置通知(方法执行前触发)@AfterReturning返回通知(方法正常返回后触发)@Around环绕通知(包裹目标方法执行)@AfterThrowing异常通知(方法抛出异常时触发)@After最终通知(类似finally块)@EnableAspectJAutoProxy启用AspectJ自动代理高级用法示例
自定义注解实现切面
java
// 1. 定义自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {
String action;
}
// 2. 在切面中使用注解切入点
@Aspect
@Component
public class AuditAspect {
@Around("@annotation(auditLog)")
public Object audit(ProceedingJoinPoint pjp, AuditLog auditLog) throws Throwable {
System.out.println("审计动作: " + auditLog.action);
return pjp.proceed;
}
}
// 3. 在服务方法上使用
@Service
public class PaymentService {
@AuditLog(action = "支付操作")
public void processPayment {
System.out.println("处理支付...");
}
}
执行顺序控制
java
@Aspect
@Component
@Order(1) // 数字越小优先级越高
public class FirstAspect {
@Before("execution(* com.example.service.*.*(..))")
public void firstAdvice {
System.out.println("第一个切面");
}
}
@Aspect
@Component
@Order(2)
public class SecondAspect {
@Before("execution(* com.example.service.*.*(..))")
public void secondAdvice {
System.out.println("第二个切面");
}
}
常见问题解决
切面不生效检查清单:Ø 切面类需有 @Component 或 @Aspect
Ø 包扫描路径包含切面类和目标类
Ø 目标方法必须是public(Spring AOP限制)
Ø 目标方法需通过Spring代理调用(直接调用内部方法不生效)
获取方法参数:java
@Before("serviceLayer")
public void logParams(JoinPoint joinPoint) {
Object args = joinPoint.getArgs;
// 处理参数...
修改返回值(环绕通知):java
@Around("serviceLayer")
public Object modifyResult(ProceedingJoinPoint pjp) throws Throwable {
Object result = pjp.proceed;
if(result instanceof String) {
return ((String) result).toUpperCase;
}
return result;
}
Spring AOP 的注解实现提供了声明式的切面编程能力,结合自定义注解可轻松实现日志记录、性能监控、事务管理等横切关注点,大幅提升代码复用性和可维护性。
来源:老客数据一点号