Java案例如何实现环绕通知?

wen java案例 25

本文目录导读:

Java案例如何实现环绕通知?

  1. 基础环境搭建
  2. 创建环绕通知
  3. 目标业务类
  4. 自定义注解
  5. 测试类
  6. 关键知识点
  7. 注意事项

在Java中,实现环绕通知最主流的做法是使用Spring AOP的@Around注解,环绕通知可以在目标方法执行前后添加自定义逻辑,甚至可以完全控制目标方法的执行。

基础环境搭建

确保项目引入Spring AOP依赖:

Maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

启用AOP

在配置类上添加@EnableAspectJAutoProxy注解:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}

创建环绕通知

示例1:简单的性能监控切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class PerformanceAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        // 前置逻辑:记录开始时间
        long startTime = System.currentTimeMillis();
        System.out.println("开始执行方法: " + joinPoint.getSignature().getName());
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            return result;
        } finally {
            // 后置逻辑:计算执行时间
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            System.out.println("方法 " + joinPoint.getSignature().getName() + 
                             " 执行耗时: " + duration + "ms");
        }
    }
}

示例2:日志记录切面

@Aspect
@Component
public class LoggingAspect {
    @Around("execution(* com.example.controller.*.*(..))")
    public Object logMethodCall(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法参数
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("调用方法: " + methodName);
        System.out.println("参数: " + Arrays.toString(args));
        Object result = null;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
            System.out.println("方法返回: " + result);
            return result;
        } catch (Exception e) {
            System.out.println("方法异常: " + e.getMessage());
            throw e;
        } finally {
            System.out.println("方法执行完毕: " + methodName);
        }
    }
}

示例3:权限验证切面

@Aspect
@Component
public class SecurityAspect {
    @Around("@annotation(com.example.annotation.RequireAuth)")
    public Object checkAuthentication(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取当前用户信息(假设从ThreadLocal获取)
        User currentUser = SecurityContext.getCurrentUser();
        if (currentUser == null) {
            throw new SecurityException("用户未登录");
        }
        // 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        RequireAuth annotation = signature.getMethod().getAnnotation(RequireAuth.class);
        String requiredRole = annotation.role();
        // 检查权限
        if (!currentUser.hasRole(requiredRole)) {
            throw new SecurityException("权限不足");
        }
        // 权限验证通过,执行目标方法
        return joinPoint.proceed();
    }
}

目标业务类

@Service
public class UserService {
    public User findUserById(Long id) {
        System.out.println("查询用户ID: " + id);
        // 模拟耗时操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return new User(id, "张三");
    }
}
@RestController
@RequestMapping("/api/users")
public class UserController {
    @GetMapping("/{id}")
    @RequireAuth(role = "ADMIN")
    public User getUser(@PathVariable Long id) {
        return new User(id, "张三");
    }
}

自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireAuth {
    String role() default "USER";
}

测试类

@SpringBootTest
@RunWith(SpringRunner.class)
public class AopTest {
    @Autowired
    private UserService userService;
    @Test
    public void testAroundAdvice() {
        userService.findUserById(1L);
    }
}

关键知识点

ProceedingJoinPoint 常用方法

方法 说明
proceed() 执行目标方法
proceed(Object[] args) 用指定的参数执行目标方法
getArgs() 获取方法参数数组
getSignature() 获取方法签名
getTarget() 获取目标对象
getThis() 获取代理对象

环绕通知执行流程

@Around("pointcut")
public Object around(ProceedingJoinPoint pjp) {
    // 1. 前置处理
    // ...
    try {
        // 2. 执行目标方法
        Object result = pjp.proceed();
        // 3. 后置处理(方法正常返回)
        // ...
        return result;
    } catch (Exception e) {
        // 4. 异常处理
        // ...
        throw e;
    }
}

注意事项

  1. 必须调用proceed():如果环绕通知不调用proceed(),目标方法将不会执行
  2. 返回结果处理:环绕通知必须返回目标方法的执行结果,否则调用方将收到null
  3. 异常传播:异常应该继续向上抛出,除非你想改变异常类型
  4. 线程安全:确保环绕通知中的共享资源访问是线程安全的

环绕通知是最强大的通知类型,因为它可以完全控制目标方法的执行过程,适合用于事务管理、日志记录、性能监控、安全控制等场景。

抱歉,评论功能暂时关闭!