AOP中After和AfterReturning的区别

本文深入解析Spring AOP的实现机制,包括前置、后置、环绕、抛出和最终通知的运用,通过具体代码示例展示如何配置和使用AspectJ进行切面编程。

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

样例代码:
原始类:

@Repository
public class UserDaoImp implements UserDao {
    @Override
    public int addUser() {
        System.out.println("add user ......");
        return 6666;
    }

    @Override
    public void updateUser() {
        System.out.println("update user ......");
    }

    @Override
    public void deleteUser() {
        System.out.println("delete user ......");
    }

    @Override
    public void findUser() {
        System.out.println("find user ......");
    }
}
@Aspect
public class MyAspect {

    /**
     * 前置通知
     */
    @Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void before(){
        System.out.println("前置通知....");
    }

    /**
     * 后置通知
     * returnVal,切点方法执行后的返回值
     */
    @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
    public void AfterReturning(Object returnVal){
        System.out.println("后置通知...."+returnVal);
    }


    /**
     * 环绕通知
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     * @param e
     */
    @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
    public void afterThrowable(Throwable e){
        System.out.println("出现异常:msg="+e.getMessage());
    }

    /**
     * 无论什么情况下都会执行的方法
     */
    @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void after(){
        System.out.println("最终通知....");
    }
}

编写配置文件交由Spring IOC容器管理

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 启动@aspectj的自动代理支持-->
    <aop:aspectj-autoproxy />

    <!-- 定义目标对象 -->
    <bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
    <!-- 定义aspect类 -->
    <bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
</beans>

编写测试类:

/**
 * Created by zejian on 2017/2/19.
 * Blog : http://blog.youkuaiyun.com/javazejian [原文地址,请尊重原创]
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= "classpath:spring/spring-aspectj.xml")
public class UserDaoAspectJ {
    @Autowired
    UserDao userDao;

    @Test
    public void aspectJTest(){
        userDao.addUser();
    }
}
————————————————
版权声明:本文为优快云博主「zejian_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/javazejian/article/details/56267036

结果输出:

结论:

@After 被代理的方法执行完成之后,要执行的代码。

@AfterReturning 新生成的代理方法执行完成之后(return 之后),要执行的代码,是放在finally块中的。

备注:

很多人在评论区认为结论写反了, 可能是由于Spring官方文档也说After是最终(finally)通知,应该被最后执行。

Types of advice:

  • Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).

  • After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.

  • After throwing advice: Advice to be executed if a method exits by throwing an exception.

  • After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).

为此,我拿aspectj的工具写了测试代码,如下:

public class Boss {

    public static void main(String[] args) {
        Boss boss = new Boss();
        boss.meeting();
    }

    public void meeting() {
        System.out.println("The boss is in a meeting");
    }

}


public aspect BossAspect{
	//定义切点
    pointcut bossPoint() : execution(* meeting(..));
	
    //前置通知,对切点增强
	before() : bossPoint(){
		System.out.println("before advise");
	}

	//after通知
	after() : bossPoint(){
		System.out.println("after advise");
	}

	//after returning通知
	after() returning: bossPoint(){
		System.out.println("after returning advise");
	}
	
}

然后执行如下命令编译:

./ajc ../src/*.java -injars /Users/tt0008641/IdeaProjects/aspectj/lib/aspectjrt.jar

然后对生成的Boss.class反编译,得到如下结果:

public class Boss {
    public Boss() {
    }

    public static void main(String[] args) {
        Boss boss = new Boss();
        boss.meeting();
    }

    public void meeting() {
        try {
            BossAspect.aspectOf().ajc$before$BossAspect$1$7ba14e64();
            System.out.println("The boss is in a meeting");
        } catch (Throwable var2) {
            BossAspect.aspectOf().ajc$after$BossAspect$2$7ba14e64();
            throw var2;
        }

        BossAspect.aspectOf().ajc$after$BossAspect$2$7ba14e64();
        BossAspect.aspectOf().ajc$afterReturning$BossAspect$3$7ba14e64();
    }
}

 可以看到,最后两行代码就是Aspectj为我们添加的代码,一个是AfterAdvise的织入代码,一个是AfterReturning织入的代码。他们都没有放入finally块中,换句话说没有finally块。他们都是在被代理方法执行完成之后执行的,只是after织入方法先被执行,after returning后被执行。而且通过反编译代码可以发现,如果有异常,在catch块中接着继续抛出异常,after和afterreturning的方法(上边最后两行)代码都不被执行。

当然在Spring中,并没有使用Aspect 的编译器来编译,而只是借鉴了他的语法,同时使用jdk动态代理或者cglib的方式来生成代理对象,至于生成的对象长什么样子,被代理的方法和各种织入的代码之间的执行顺序,大家有兴趣可以继续深入探讨一下(由于spring是直接在内存中生成代理对象的字节码,我们没办法找到对应的文件进行反编译哈),我看网上有的文章说当异常发生时,所有的织入代码都会被执行,顺序是这样的顺,对此保持怀疑啊,大家有兴趣可以继续研究下:

有异常的情况

@Around start
@Before...
method invoke
@After...
@AfterThrowing...  


try{
    try{
        //@Around start
        //@Before
        method.invoke(..);
        //@Around end
    }finally{
        //@After
    }
    //@AfterReturning
}catch(){
    //@AfterThrowing

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值