Spring AOP注解实现详解
Spring AOP注解实现详解
一、核心注解概述
Spring AOP通过以下核心注解实现面向切面编程:
@Aspect:声明切面类
@Pointcut:定义切入点表达式
通知注解:
@Before:前置通知
@After:后置通知
@AfterReturning:返回后通知
@AfterThrowing:异常通知
@Around:环绕通知
二、基础配置
启用AOP注解支持
@Configuration
@EnableAspectJAutoProxy // 启用AOP注解支持
public class AppConfig {
定义切面类
@Aspect
@Component
public class LoggingAspect {
// 定义切入点表达式
@Pointcut("execution( com.example.service..*(..))")
private void serviceLayer() {}
三、通知类型详解
前置通知(@Before)
@Before(“serviceLayer()”)
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("方法调用前: " + joinPoint.getSignature().getName());
System.out.println("参数: " + Arrays.toString(joinPoint.getArgs()));
后置通知(@After)
@After(“serviceLayer()”)
public void afterAdvice(JoinPoint joinPoint) {
System.out.println("方法执行完成: " + joinPoint.getSignature().getName());
返回后通知(@AfterReturning)
@AfterReturning(
pointcut = “serviceLayer()”,
returning = “result”
)
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
System.out.println("方法返回: " + joinPoint.getSignature().getName());
System.out.println("返回值: " + result);
异常通知(@AfterThrowing)
@AfterThrowing(
pointcut = “serviceLayer()”,
throwing = “ex”
)
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
System.out.println("方法抛出异常: " + joinPoint.getSignature().getName());
System.out.println("异常信息: " + ex.getMessage());
环绕通知(@Around)
@Around(“serviceLayer()”)
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前: " + joinPoint.getSignature().getName());
try {
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕后: 方法正常返回");
return result;
catch (Exception e) {
System.out.println("环绕后: 方法抛出异常");
throw e;
}
四、切入点表达式
常用表达式语法
表达式 说明
execution( com.example.service…(…)) service包下所有类的所有方法
execution(public (…)) 所有public方法
execution( set(…)) 所有以set开头的方法
within(com.example.service.) service包下的所有类
@annotation(com.example.Loggable) 带有@Loggable注解的方法
组合表达式
@Pointcut(“execution( com.example.service…(…)) && !execution( com.example.service.UserService.*(…))”)
public void serviceLayerExceptUserService() {}
五、高级特性
引入(Introduction)
@Aspect
@Component
public class UsageTrackingAspect {
@DeclareParents(
value = "com.example.service.*+",
defaultImpl = DefaultUsageTracked.class
)
public static UsageTracked mixin;
public interface UsageTracked {
void incrementUseCount();
public class DefaultUsageTracked implements UsageTracked {
private int useCount = 0;
@Override
public void incrementUseCount() {
useCount++;
}
参数绑定
@Before(“serviceLayer() && args(name)”)
public void beforeAdviceWithParam(JoinPoint joinPoint, String name) {
System.out.println("方法参数name: " + name);
六、性能优化建议
减少切入点表达式复杂度:复杂表达式会增加匹配时间
合理选择通知类型:优先使用@Around,减少代理链长度
避免在切面中执行耗时操作:会影响所有被代理方法
使用@Order指定切面顺序:避免不必要的执行顺序调整
七、常见问题解决
切面不生效
可能原因:
未添加@EnableAspectJAutoProxy
切面类未被Spring管理(缺少@Component等注解)
切入点表达式不匹配目标方法
自调用问题
Spring AOP基于代理实现,类内部方法调用不会触发切面逻辑。解决方案:
// 通过AopContext获取当前代理对象
((UserService)AopContext.currentProxy()).internalMethod();
八、实际应用案例
日志记录切面
@Aspect
@Component
public class LoggingAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
public void controllerLayer() {}
@Around("controllerLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("Enter: {}.{}()",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName());
try {
Object result = joinPoint.proceed();
logger.info("Exit: {}.{}() with result = {}",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(),
result);
return result;
catch (Exception e) {
logger.error("Exception in {}.{}() with cause = {}",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(),
e.getCause() != null ? e.getCause() : "NULL");
throw e;
}
性能监控切面
@Aspect
@Component
public class PerformanceAspect {
@Around("@annotation(com.example.MonitorPerformance)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
finally {
long executionTime = System.currentTimeMillis() - startTime;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
}
通过以上注解实现,Spring AOP可以优雅地实现横切关注点,保持业务代码的整洁性。
- 点赞
- 收藏
- 关注作者
评论(0)