美文网首页
Spring Aop切面的五种通知

Spring Aop切面的五种通知

作者: 饱饱想要灵感 | 来源:发表于2024-09-18 15:08 被阅读0次

    一、概述

    spring aop通知(advice)分成五类:

    1. 前置通知Before advice:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
    2. 正常返回通知After returning advice:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
    3. 异常返回通知After throwing advice:在连接点抛出异常后执行。
    4. 后置通知After (finally) advice:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
    5. 环绕通知Around advice:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。

    关于切面表达式可参考博客spring aop切面表达式详解及例子

    二、五种通知的执行顺序

    • 在方法执行正常时:

      1. 环绕通知@Around
      2. 前置通知@Before
      3. 方法执行
      4. 环绕通知@Around
      5. 后置通知@After
      6. 正常返回通知@AfterReturning
    • 在方法执行抛出异常时:

      这个时候顺序会根据环绕通知的不同而发生变化:

      • 环绕通知捕获异常并不抛出:
        1. 环绕通知@Around
        2. 前置通知@Before
        3. 方法执行
        4. 环绕通知@Around
        5. 后置通知@After
        6. 正常返回通知@AfterReturning
      • 环绕通知捕获异常并抛出:
        1. 环绕通知@Around
        2. 前置通知@Before
        3. 方法执行
        4. 环绕通知@Around
        5. 后置通知@After
        6. 异常返回通知@AfterThrowing

    三、代码示例

    1. 创建服务

      • 创建package命名为com.popo.service

      • 创建接口IHelloService,内容如下

        public interface IHelloService {
            void sayHello();
        
            void say(String msg);
        
            void err(boolean isThrow);
        }
        
        
      • 创建package命名为com.popo.service.impl

      • 创建接口IHelloService的实现类HelloServiceImpl,内容如下

        package com.popo.service.impl;
        
        import com.popo.service.IHelloService;
        import org.springframework.stereotype.Service;
        
        @Service
        public class HelloServiceImpl implements IHelloService {
            @Override
            public void sayHello() {
                System.out.println("hello");
            }
        
            @Override
            public void say(String msg) {
                System.out.println(msg);
            }
        
            @Override
            public void err(boolean isThrow) {
                System.out.println("error begin");
                throw new RuntimeException("sss");
            }
        }
        
        
    2. 创建AOP

      • 创建package命名为com.popo.aop

      • 配置AOP,新建ExecutionAOP,内容如下

        @Aspect
        @Component
        public class ExecutionAop {
        
            // 切点范围
            @Pointcut("execution(* com.popo.service..*(..))")
            public void pointcut(){ }
        
            @Before("pointcut()")
            public void before() {
                System.out.println("---------------@Before----------------");
            }
        
            @AfterReturning("pointcut()")
            public void afterReturning() {
                System.out.println("---------------@AfterReturning----------------");
            }
        
            @After("pointcut()")
            public void after() {
                System.out.println("---------------@After----------------");
            }
        
            @AfterThrowing("pointcut()")
            public void afterThrowing() {
                System.out.println("---------------@AfterThrowing----------------");
            }
        
            @Around("pointcut()")
            public Object around(ProceedingJoinPoint pjp) throws Throwable {
                Object result;
                System.out.println("---------------@Around前----------------");
                try {
                    result = pjp.proceed();
                } catch (Throwable throwable) {
                    System.out.println("---------------@Around异常----------------");
                    // 监听参数为true则抛出异常,为false则捕获并不抛出异常
                    if (pjp.getArgs().length > 0 && !(Boolean) pjp.getArgs()[0]) {
                        result = null;
                    } else {
                        throw throwable;
                    }
                }
                System.out.println("---------------@Around后----------------");
                return result;
            }
        
            // Around简单示例
        //    @Around("pointcut()")
        //    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //        Object result;
        //        System.out.println("---------------@Around前----------------");
        //        result = pjp.proceed();
        //        System.out.println("---------------@Around后----------------");
        //        return result;
        //    }
        
        }
        
        
    3. 创建测试用例

      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class ApplicationTests {
      
          @Resource
          private IHelloService helloService;
      
          @Test
          public void test1() {
              helloService.sayHello();
          }
      
          @Test
          public void test2() {
              helloService.say("hello");
          }
      
          @Test
          public void test3() {
              System.out.println("begin");
              helloService.err(true);
              System.out.println("end");
          }
      
          @Test
          public void test4() {
              System.out.println("begin");
              helloService.err(false);
              System.out.println("end");
          }
      }
      
      
      • 执行test1可得到结果

        ---------------@Around前----------------
        ---------------@Before----------------
        hello
        ---------------@Around后----------------
        ---------------@After----------------
        ---------------@AfterReturning----------------
        
        
      • 执行test2结果与test1一样

      • 执行test3可得到结果(在环绕通知中抛出异常)

        begin
        ---------------@Around前----------------
        ---------------@Before----------------
        error begin
        ---------------@Around异常----------------
        ---------------@After----------------
        ---------------@AfterThrowing----------------
        
        java.lang.RuntimeException: sss
        ...
        
        
      • 执行test4可得到结果(在环绕通知中不抛出异常)

        begin
        ---------------@Around前----------------
        ---------------@Before----------------
        error begin
        ---------------@Around异常----------------
        ---------------@Around后----------------
        ---------------@After----------------
        ---------------@AfterReturning----------------
        end
        
        

    四、多个AOP之间的执行顺序

    1. 使用注解@Order
    2. 实现接口Ordered

    五、结论

    Spring AOP就像一个同心圆,要执行的方法为圆心,最外层的order最小,环绕、前置通知先执行,后置、返回通知后执行。

    相关文章

      网友评论

          本文标题:Spring Aop切面的五种通知

          本文链接:https://www.haomeiwen.com/subject/rqvnljtx.html