Spring AOP 的 afterReturing 为什么不能改变返回值

本文探讨了Spring AOP中@AfterReturning注解的使用限制,通过示例代码详细解释为何该注解无法修改方法的返回值。

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

    之前在开发过程中使用 @AfterReturning ,想要在切面方法中改变目标方法的返回值,但是却发现这样做不行。当时没有想到原因,后来猜测 @AfterReturing 标记的切面方法可能在 finnally 块中被调用所以不能改变返回值。今天经过测试发现这个猜测是错误的,在 finally 块中是可以改变返回值的,因为此时方法还没有返回。

public class FinallyAlterReturnValDemo {


    public static String saySomething(){

        return "say something ...";
    }

    public static Object invoke(){
        String val;
        try {
            val =  saySomething();
            return val;
        }catch (Exception e){

        }finally {
            val = "changed return value";
            return val;
        }
    }

    public static void main(String[] args) {

        Object invoke = invoke();
        System.out.println(invoke);
        // 执行结果是 changed return value
    }
}

    对Spring AOP 的 @AfterReturning 进行测试

public interface Say {

    String saySomething();
}
@Component
public class Target implements Say{

    @Override
    public String saySomething(){
        return "say something ... ";
    }
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * Created by j4love on 2016/12/22.
 * Description :
 */

@Component
@org.aspectj.lang.annotation.Aspect
@EnableAspectJAutoProxy()
public class Aspect {

    @Pointcut(value = "execution(* kernel.spring.AOP.bean.*.*(..))")
    public void pointcut(){}

    @AfterReturning(pointcut = "pointcut()",returning = "retVal")
    public Object afterReturning(JoinPoint jp,Object retVal){

        System.out.println("afterReturning ---- " + retVal);
        return "change ... " + retVal;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="kernel.spring.AOP"  />

</beans>
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/**
 * Created by j4love on 2016/12/22.
 * Description :
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:conf/spring/applicationContext-aop.xml")
public class TargetTest {

    @Autowired
    private Say target;

    @Test
    public void saySomething() throws Exception {

//        Object o = AopContext.currentProxy();
        System.out.println("isJdkProxy = " + AopUtils.isJdkDynamicProxy(this.target));
        System.out.println("isCglibProxy = " + AopUtils.isCglibProxy(this.target));
        String s = this.target.saySomething();
        System.out.println(s);
    }

}

AfterReturning 确实是无法改变返回值的,查看源码发现原因,下面贴出关键源码。

package org.springframework.aop.framework.adapter;

import java.io.Serializable;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.util.Assert;

/**
 * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
 * Used internally by the AOP framework; application developers should not need
 * to use this class directly.
 *
 * @author Rod Johnson
 */
@SuppressWarnings("serial")
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

	private final AfterReturningAdvice advice;


	/**
	 * Create a new AfterReturningAdviceInterceptor for the given advice.
	 * @param advice the AfterReturningAdvice to wrap
	 */
	public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
		Assert.notNull(advice, "Advice must not be null");
		this.advice = advice;
	}

	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

}

程序运行,目标方法被调用后,会调用 invoke(MethodInvocation mi) throws Throwable 方法,这里就会去调用目标方法,也就是说目标方法在这里就执行完了。retVal 就是目标方法的返回值。而 afterReturing 是在目标方法执行返回之后才去调用的,并且 aftreReturing 方法返回 void 所以,尽管我在@AfterReturing 标记的切面方法中有返回值也没什么作用。整个 invoke 方法最终返回的是目标方法的返回值 retVal 。这下把 @afterReturing 为什么没法改变返回值搞清楚了。

转载于:https://my.oschina.net/j4love/blog/810115

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值