Spring @Around 注解

@Around 是 Spring AOP(面向切面编程)中的一个注解,它用于定义一个环绕通知(Around Advice)。环绕通知是 AOP 中最强大的一种通知类型,因为它能够在方法执行之前和之后都执行自定义的逻辑,并且可以控制方法是否继续执行或改变其返回值。

@Around 注解的基本用法

要使用 @Around 注解,你需要先定义一个切面(Aspect),然后在该切面中使用 @Around 注解来标注一个方法,该方法将作为环绕通知。环绕通知方法必须接受一个 ProceedingJoinPoint 类型的参数,这个参数提供了对正在执行的方法的访问。

以下是一个简单的例子:

java 复制代码
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 MyAroundAdvice {

    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        // 在方法执行之前的逻辑
        System.out.println("Before method execution");

        try {
            Object result = pjp.proceed(); // 执行目标方法
            // 在方法执行之后的逻辑
            System.out.println("After method execution");
            return result; // 返回目标方法的执行结果
        } catch (Throwable throwable) {
            // 处理异常
            System.out.println("Exception occurred: " + throwable.getMessage());
            throw throwable; // 重新抛出异常
        }
    }
}

@Around 注解的工作机制

  1. 匹配切入点 ‌:@Around 注解的值是一个切入点表达式(Pointcut Expression),它指定了哪些方法将被这个环绕通知拦截。在上面的例子中,切入点表达式 "execution(* com.example.service.*.*(..))" 匹配了 com.example.service 包下所有类的所有方法。

  2. 创建代理‌:Spring AOP 会在运行时为匹配到的方法创建代理对象。当这些方法被调用时,实际上调用的是代理对象的方法。

  3. 执行环绕通知 ‌:当代理对象的方法被调用时,Spring AOP 会先执行环绕通知中的逻辑。在环绕通知中,你可以通过调用 ProceedingJoinPointproceed 方法来执行目标方法。你也可以选择不调用 proceed 方法,从而阻止目标方法的执行。

  4. 处理返回值和异常‌:环绕通知可以获取目标方法的返回值,并在必要时进行修改。同时,它也可以捕获目标方法抛出的异常,并进行相应的处理。

以下是一些常见的@Around注解应用场景:

  1. 日志记录‌:在目标方法执行之前和之后记录日志,可以帮助开发者了解方法的执行流程、输入参数、返回值以及执行时间等信息。这对于调试和监控应用程序的状态非常有帮助。

  2. 性能监控‌:通过在目标方法执行前后记录时间戳,可以计算出方法的执行时间。这对于性能优化和瓶颈识别非常重要,可以帮助开发者找出需要优化的代码段。

  3. 事务管理‌:在目标方法执行之前开启一个事务,并在方法执行成功后提交事务;如果方法执行失败,则回滚事务。这样可以确保数据的一致性和完整性。

  4. 缓存管理‌:在目标方法执行之前检查缓存中是否存在所需的数据;如果存在,则直接返回缓存中的数据,避免重复执行耗时的方法;如果不存在,则执行方法并将结果存入缓存中。

  5. 权限检查‌:在目标方法执行之前检查当前用户是否具有执行该方法的权限;如果没有权限,则抛出异常或返回错误提示,从而保护系统的安全性。

  6. 异常处理‌:捕获目标方法抛出的异常,并进行统一处理,如记录异常日志、发送报警通知或返回友好的错误提示给用户。这可以提高系统的健壮性和用户体验。

  7. 输入参数校验‌:在目标方法执行之前对输入参数进行校验,确保参数的有效性和合法性。如果参数无效或不合法,则抛出异常或返回错误提示,避免执行无效的操作。

  8. 结果后处理‌:在目标方法执行之后对返回结果进行后处理,如格式化数据、转换数据类型或添加额外的信息。这可以满足不同的业务需求和数据展示要求。

综上所述,@Around注解在Spring AOP中具有广泛的应用场景,它能够帮助开发者在不修改目标方法代码的情况下实现各种复杂的功能和逻辑。通过合理地使用@Around注解,可以提高代码的可读性、可维护性和可扩展性。

@Around 注解常用场景实例

权限校验

以下是一个使用@Around注解进行权限校验的示例代码:

java 复制代码
@Around("@annotation(authCheck)")
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
    String mustRole = authCheck.mustRole();
    RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
    HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
    User loginUser = userService.getLoginUser(request);
    if (!loginUser.hasRole(mustRole)) {
        throw new AccessDeniedException("用户没有权限执行此操作");
    }
    return joinPoint.proceed(); // 继续执行目标方法
}

在这个示例中,doInterceptor方法会在被@AuthCheck注解标记的方法执行前后运行。首先检查用户是否有足够的权限,如果没有权限则抛出异常,否则继续执行目标方法‌。

日志记录和性能监控

以下是一个使用@Around注解的具体实例,该实例展示了如何在Spring AOP中实现日志记录和性能监控的功能。

首先,我们需要定义一个切面类,并在其中使用@Around注解来标注环绕通知方法。

java 复制代码
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 LoggingAndPerformanceAspect {

    @Around("execution(* com.example.service.*.*(..))")
    public Object logAndMeasureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 记录方法开始执行的时间
        long startTime = System.currentTimeMillis();

        // 记录日志:方法开始执行
        System.out.println("Method " + joinPoint.getSignature().getName() + " is starting with arguments: " + Arrays.toString(joinPoint.getArgs()));

        try {
            // 执行目标方法
            Object result = joinPoint.proceed();

            // 记录方法执行结束的时间
            long endTime = System.currentTimeMillis();

            // 记录日志:方法执行结束,并输出执行时间
            System.out.println("Method " + joinPoint.getSignature().getName() + " executed in " + (endTime - startTime) + " milliseconds with result: " + result);

            // 返回目标方法的执行结果
            return result;
        } catch (Throwable throwable) {
            // 记录异常日志
            System.out.println("Exception occurred in method " + joinPoint.getSignature().getName() + ": " + throwable.getMessage());

            // 重新抛出异常,以便上层调用者处理
            throw throwable;
        }
    }
}

在这个例子中,LoggingAndPerformanceAspect是一个切面类,它包含了一个使用@Around注解标注的环绕通知方法logAndMeasureExecutionTime。这个方法会在匹配到的方法执行之前和之后分别记录日志,并测量方法的执行时间。

接下来,我们需要确保Spring容器能够识别这个切面类,并将其应用到相应的目标方法上。通常,这可以通过在Spring配置文件中启用AOP自动代理或者通过@EnableAspectJAutoProxy注解来实现。

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

    // 其他Bean定义...

    // 确保LoggingAndPerformanceAspect被Spring容器管理
    @Bean
    public LoggingAndPerformanceAspect loggingAndPerformanceAspect() {
        return new LoggingAndPerformanceAspect();
    }
}

现在,当Spring容器中的任何匹配到切入点表达式"execution(* com.example.service.*.*(..))"的方法被执行时,logAndMeasureExecutionTime环绕通知都会被触发,从而记录日志和测量执行时间。

例如,如果你有一个服务类MyService,其中有一个方法doSomething,那么当这个方法被调用时,你会在控制台看到类似以下的输出:

java 复制代码
Method doSomething is starting with arguments: [...]
Method doSomething executed in XXX milliseconds with result: ...

这样,你就成功地使用@Around注解实现了日志记录和性能监控的功能。

相关推荐
lead520lyq几秒前
Golang Grpc接口调用实现账号密码认证
开发语言·后端·golang
EQ-雪梨蛋花汤几秒前
【问题反馈】JNI 开发:为什么 C++ 在 Debug 正常,Release 却返回 NaN?
开发语言·c++
naruto_lnq1 分钟前
高性能消息队列实现
开发语言·c++·算法
小北方城市网1 分钟前
MongoDB 分布式存储与查询优化:从副本集到分片集群
java·spring boot·redis·分布式·wpf
charlie1145141912 分钟前
malloc 在多线程下为什么慢?——从原理到实测
开发语言·c++·笔记·学习·工程实践
想逃离铁厂的老铁3 分钟前
Day60 >> 94、城市间货物运输1️⃣ + 95、城市间货物运输 2️⃣ + 96、城市间货物运输 3️⃣
java·服务器·前端
kyrie学java5 分钟前
SpringWeb
java·开发语言
写代码的【黑咖啡】6 分钟前
Python 中的 Gensim 库详解
开发语言·python
TTBIGDATA1 小时前
【Hue】Ambari 页面启动 Hue 失败 user ‘hadoop‘ does not exist
java·hadoop·ambari
饺子大魔王的男人2 小时前
Remote JVM Debug+cpolar 让 Java 远程调试超丝滑
java·开发语言·jvm