Spring AOP之@Around,@AfterReturning使用、切不进去问题解决

本文介绍了如何使用AOP在不修改核心模块代码的情况下进行功能增强,包括@AfterReturning、@Before和@Around的使用案例,以及解决常见问题和bug的技巧。通过AOP,可以在方法执行前后插入自定义逻辑,例如打印日志、篡改参数或返回值。同时,文章提到了AOP在实际操作中可能出现的切面无法切入、代码跳转失效等问题及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文主要举几个工作中典型AOP的实操案例,还有经常出现的问题(切不进去,ctrl+左键跳不到被切方法中)等等。

本文对于AOP的实现原理概不讨论,百度一搜有的是。

AOP的使用背景和好处

比如A模块是公司的核心模块,这块代码未经允许不得轻易篡改。

但是你又有新的需求,需要在公司的核心模块的某个方法上进行增强(比如在执行核心方法的之前打印自定义日志,或者修改该核心方法的入参和返回值等等)

这样你就可以在不修改核心模块源码的情况下,对源代码的方法进行增强,扩展原来方法的一些功能。

这样既能保证源代码不被破坏,又可以扩展源代码现有的功能。

一、几种使用姿势

1、@AfterReturning和@Before

@AfterReturning是后置方法,在目标方法执行后执行,@Before是前置方法,在目标方法执行前执行

它们一般配合JoinPoint来使用(不能配合ProceedingJoinPoint,会报错)。直接看例子:

被切的方法:

@Service
public class OriFuncImpl implements OriFunc{

    @Override
    public String ori(String str){
        System.out.println("执行了原方法");
        return str;
    }
}

使用@AfterReturning 和 @Before:

@Aspect
@Component
public class AopFunc {
    @Pointcut("execution(* com.daji.aop_test.OriFuncImpl.ori(..))")
    public void test() {
    }

    @Before("test()")
    public void before(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();    //获取方法入参
        System.out.println("原方法的入参是:"+args[0]);
        System.out.println("原方法执行前会先执行我!!");
    }


    @AfterReturning("test()")
    public void after(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();    //获取方法入参
        System.out.println("原方法执行后会执行我!!");
    }

}

如果遇到异常,则不执行。

当连接点方法成功执行后,返回通知方法才会执行,如果连接点方法出现异常,则返回通知方法不执行。返回通知方法在目标方法执行成功后才会执行,所以,返回通知方法可以拿到目标方法(连接点方法)执行后的结果。

@AfterReturning获取被切方法返回值,篡改返回参数:
在注解中增加returning参数即可: returning = “methodResult”

@Pointcut("execution(* com.daji.aop_test.AopTestController.test1(..))")
    public void publish() {
    }

    @AfterReturning(value = "publish()",returning = "methodResult")
    public Object afterReturningPublish(JoinPoint joinPoint, Object methodResult) {
        //获取方法返回值
        String returnJson = JSONObject.toJSONString(methodResult);
        Object[] args = joinPoint.getArgs();
        System.out.println("原方法执行后会执行我!!");
        //这个返回值可以被我们篡改。
        return methodResult;
    }

其实这个返回值也不是能任意篡改的:

答案来了:可以改变返回值,但是分情况,
不能改变:
第一种情况:如果返回的对象,改变了对象的引用地址,这种情况,是不能改变返回对象中的值的
第二种情况:如果返回的对象是一个基本数据类型,或者是String的值,是不能改变返回值的,尤其是String这种final类型的。
可以改变:
直接使用传入的object对象,改变其中的值,是可以的。

2、@Around

@Around是环绕通知,既可以控制入参,还可以控制原方法的执行和返回值

常常配合ProceedingJoinPoint来使用。直接看例子:

被切的方法:

@Service
public class OriFuncImpl implements OriFunc{

    @Override
    public String ori(String str){
        System.out.println("执行了原方法");
        return str;
    }
}

使用@Around:

@Aspect
@Component
public class AopFunc {
    @Pointcut("execution(* com.daji.aop_test.OriFuncImpl.ori(..))")
    public void modifyReturn() {
    }

    @Around("modifyReturn()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        Object result = joinPoint.proceed(args);
        return result;
    }

}

3、@Around可以篡改返回值,篡改入参

需要ProceedingJoinPoint的配合

注意一定要将@Around修饰的方法用Object修饰其返回值,并且返回原方法执行的结果,如下图所示:

在这里插入图片描述
篡改入参一样的道理,只需要篡改下图中的 args数组,然后让其传入proceed中,即可完成篡改入参。如下图所示:

在这里插入图片描述

所以,这个@Around比较万能,尤其是配合ProceedingJoinPoint的使用。使AOP能做的事情更多了。

引申一下JoinPoint 和 ProceedingJoinPoint的关系:

  • ProceedingJoinPoint 只能在@Around中使用

  • JoinPoint也可以获取入参(getArgs()),它可以用于@Before 和 @AfterReturning

  • Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。它们之间的关系如下图:

在这里插入图片描述

4、@Around如果不执行proceed(),那么原方法将不会执行

二、使用AOP常见的问题和bug

1、切不进去

检查是否有如下注解:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.4</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.2</version>
</dependency>

检查完毕后检查切面类,看看有没有以下注解:

在这里插入图片描述

2、ctrl+鼠标左键不能自动跳到被切方法:

正常情况如图:

在这里插入图片描述
如果你存在上述问题,检查你有没有安装下列插件:

在这里插入图片描述
如果你是idea社区版,那么默认是没有的,你要么自己下,要么换成正式版。

### 使用 `@Around` 注解的 Spring AOP 实现 在 Spring AOP 中,`@Around` 是一种强大的通知类型,允许开发者完全控制目标方法执行的过程。通过使用 `ProceedingJoinPoint` 参数,可以在调用实际的方法之前和之后添加自定义逻辑。 下面是一个具体的例子来说明如何实现 `@Around` 建议: ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class LoggingAspect { @Around("execution(* com.example.service.*.*(..))") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { return joinPoint.proceed(); } finally { long executionTime = System.currentTimeMillis() - start; System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms"); } } } ``` 在这个例子中,当任何位于 `com.example.service` 包下的服务被调用时,都会触发这个环绕通知。它会记录下该操作花费的时间,并打印出来[^1]。 为了使上述代码生效,还需要配置好相应的切面以及确保项目已经引入了必要的依赖项。如果选择了基于注解的方式,则需确认已启用了对 AspectJ 的支持并注册了这些方面作为 Bean。 对于那些希望采用 XML 配置而非 Java Config 或者纯注解方式的人来说,在 spring-config.xml 文件里声明 aspect 可能更合适一些[^2]。 值得注意的是,除了 `@Around` 外,Spring AOP 还提供了其他几种不同类型的通知机制,如前置(`@Before`)、后置返回 (`@AfterReturning`) 和异常处理 (`@AfterThrowing`) 等等[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值