Spring AOP 学习笔记 之 常用注解
0 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>3.4.2</version>
</dependency>
要在springboot中启用AOP,需要引入spring-boot-starter-aop依赖。
1.@EnableAspectAutoProxy
在 Spring Boot 应用中,通常通过在主配置类或启动类上添加 @EnableAspectJAutoProxy 注解来启用 @AspectJ 支持。
@Configuration
@EnableAspectJAutoProxy
public class ApplicationConfiguration {
}
@EnableAspectAutoProxy 是 Spring 框架中的一个注解,用于启用基于代理的切面(AOP)功能。以下是它的主要功能和作用:
- 启用 AOP 代理
默认情况下,Spring 容器不会自动为切面创建代理对象。通过在配置类上添加 @EnableAspectAutoProxy 注解,可以显式地启用 AOP 功能。这个注解会触发 Spring 容器生成代理对象,从而支持切面拦截目标方法。
- 代理模式选择
@EnableAspectAutoProxy 提供了一个属性 proxyTargetClass,用于指定代理的实现方式:
proxyTargetClass = false(默认值):使用 JDK 动态代理。
proxyTargetClass = true:使用 CGLIB 代理。
如果目标类没有实现接口,或者需要代理类本身的方法,可以选择 CGLIB 代理。
2.@Aspect
@Aspect 是 Spring AOP 中的一个核心注解,用于定义一个切面(Aspect)。
切面是 AOP 的一个模块,它封装了横切关注点(cross-cutting concerns),例如日志记录、事务管理、安全性检查等。通过使用 @Aspect 注解,你可以将这些关注点从业务逻辑中分离出来,从而使代码更加模块化和易于维护。
下面看一个例子:
@Slf4j
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.derek.controller.*.*(..))")
public void logSignature() {
}
@Before("logSignature()")
public void logBefore(JoinPoint joinPoint) {
log.info("before advice: {} ", joinPoint.getSignature().getName());
}
@Around("logSignature()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("around into method: {}", joinPoint.getSignature().getName());
Object result = joinPoint.proceed();
log.info("around out method: {}", joinPoint.getSignature().getName());
return result;
}
@AfterReturning(pointcut = "logSignature()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
log.info("after returning advice: {}, result: {}", joinPoint.getSignature().getName(), result);
}
@AfterThrowing(pointcut = "logSignature()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
log.info("after throwing advice: {}, throwing: ", joinPoint.getSignature().getName(), exception);
}
@After("logSignature()")
public void logAfter(JoinPoint joinPoint) {
log.info("after advice: {}", joinPoint.getSignature().getName());
}
}
在LogAspect.java 文件中,@Aspect 注解用于定义一个切面类,切面类包含多个通知(Advice)和切入点(Pointcut)。
注意:定义@Aspect后,只是告诉Spring AOP要扫描该类并生成代理对象,并不会自动注入到配置容器中。所以需要在切面类上加入@Component用于将切面类声明为 Spring 管理的 Bean。
3 @Pointcut
Pointcut 是 AOP 的核心概念之一,用于定义哪些方法或代码位置需要被拦截。
通过 Pointcut 表达式,可以精确指定通知(Advice)在程序中的执行位置。
Pointcut 不直接执行任何逻辑,而是作为通知的触发条件。
定义一个切入点表达式,用于指定哪些方法会被拦截。
在例子中,@Pointcut("execution(* com.derek.controller.*.*(..))") 表示拦截 com.derek.controller 包下的所有方法。
Declaring a Pointcut :: Spring Framework
3.1 Pointcut 常用表达式
Pointcut 表达式是一种基于规则的语言,用于描述需要拦截的方法或类。常见的表达式包括:
3.2 Pointcut表达式组合
可以通过逻辑运算符(&&, ||, !)组合多个 Pointcut 表达式,实现更复杂的匹配规则。
@Pointcut("execution(* com.example.service.*.*(..)) && args(name)")
public void serviceMethodsWithArgs(String name) {}
3.3 Pointcut表达式性能差异
静态匹配:仅基于方法签名(如 execution 或 within),无需运行时检查,性能较好。
动态匹配:涉及运行时参数检查(如 args 或 @annotation),需要在方法执行时进行额外判断,性能较差。
// 静态匹配
@Pointcut("execution(* com.example.service.UserService.*(..))")
public void staticPointcut() {}
// 动态匹配
@Pointcut("args(java.lang.String)")
public void dynamicPointcut() {}
动态匹配增加了运行时开销,应尽量减少使用
4 通知(Advice)
@Before:在目标方法执行之前执行。
@After:在目标方法执行之后执行,无论是否抛出异常。
@AfterReturning:在目标方法成功返回后执行。
@AfterThrowing:在目标方法抛出异常时执行。
@Around:环绕通知,可以在目标方法执行前后插入逻辑,并且可以控制目标方法的执行。
Advice内容比较多,后面单独来一篇讲解。