Spring 13: @After最终通知 + @Pointcut切入点起别名

1年前 (2023) 程序员胖胖胖虎阿
103 0 0

@After

作用

  • 最终通知,不论程序前期执行成功还是失败,最后都要执行最终通知的切面功能

业务接口

package com.example.s04;

/**
 * 业务接口
 */
public interface SomeService {
    default String order(int orderNums){return null;}
}

业务实现类

package com.example.s04;

import org.springframework.stereotype.Service;

/**
 * 业务实现类
 */
@Service
public class SomeServiceImpl implements SomeService {
    @Override
    public String order(int orderNums) {
        System.out.println("预定图书: " + orderNums + "册");
        return "order success";
    }
}

切面类

package com.example.s04;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * 切面类
 */
@Aspect
@Component
public class SomeServiceAspect {
    /**
     * a.最终通知的方法规范
     * 1.方法权限:public
     * 2.方法返回值:void
     * 3.方法名称:自定义
     * 4.方法参数:没有,就算有也只能是JoinPoint类型
     * 5.注解:需要用@After注解来表明通知的时机
     * value参数:指定切入点表达式
     *
     * b.最终通知的示例
     *  public String order(int orderNums)
     */
    @After(value = "execution(* com.example.s04.*.*(..))")
    public void myAfter(){
        System.out.println("最终通知切面被成功调用!");
    }
}

applicationContext.xml

    <!-- 添加包扫描 -->
    <context:component-scan base-package="com.example.s04"/>
    <!-- 绑定业务功能和切面功能-->
    <aop:aspectj-autoproxy/>

测试1

  • 程序正常执行
package com.example.test;

import com.example.s04.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAfter {
    //测试最终通知
    @Test
    public void testAfter(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("s04/applicationContext.xml");
        //获取jdk动态代理对象
        SomeService agent = (SomeService) ac.getBean("someServiceImpl");
        //调用业务方法(基本业务功能 + 切面业务功能)
        String res = agent.order(10);
        System.out.println("返回结果: " + res);
    }
}

测试结果

  • 此时程序正常运行,最终通知的切面功能在目标方法执行后被调用
预定图书: 10册
最终通知切面被成功调用!
返回结果: order success

Process finished with exit code 0

测试2

  • 目标方法在执行过程中出现异常,将业务实现类做如下修改
/**
 * 业务实现类
 */
@Service
public class SomeServiceImpl implements SomeService {
    @Override
    public String order(int orderNums) {
        System.out.println("预定图书: " + orderNums + "册");
        
        //目标方法在返回值前出错
        System.out.println(1/0);
        
        return "order success";
    }
}

测试结果

  • 由执行结果可以看出,虽然程序崩溃,但是最终通知还是正常执行

Spring 13: @After最终通知 + @Pointcut切入点起别名

测试3

  • 为了对比后置通知(@AfterReturning)和最终通知(@After)的区别,演示当目标方法后是后置通知而不是最终通知的情况。将@AfterReturning篇的博文的业务实现类做如下修改
/**
 * 业务实现类
 */
@Service
public class SomeServiceImpl implements SomeService{
    @Override
    public String doSome(int orderNums) {
        System.out.println("预定图书: " + orderNums + "册");
        
        //程序在返回值前崩溃
        System.out.println(1/0);
        
        return "order success";
    }
}

测试输出

  • 在目标方法出错后,后置通知没有执行

Spring 13: @After最终通知 + @Pointcut切入点起别名

@Pointcut

作用

  • 为切入点表达式起别名

切面类

  • 将上述切面类做如下修改,此时的空方法名myCut即为自定义的切入点表达式的别名,用myCut()来替换原来的切入点表达式即可
/**
 * 切面类
 */
@Aspect
@Component
public class SomeServiceAspect {

    @After(value = "myCut()")
    public void myAfter(){
        System.out.println("最终通知切面被成功调用!");
    }

    /**
     * 为切入点表达式起别名:
     * 使用@Pointcut注解
     * value属性:切入点表达式
     * 定义一个公共访问权限,无返回值的空方法,方法名即为切入点表达式的别名
     */
    @Pointcut(value = "execution(* com.example.s04.*.*(..))")
    public void myCut(){}
}

相关文章

暂无评论

暂无评论...