一、概述
spring aop通知(advice)分成五类:
- 前置通知Before advice:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。
- 正常返回通知After returning advice:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。
- 异常返回通知After throwing advice:在连接点抛出异常后执行。
- 后置通知After (finally) advice:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
- 环绕通知Around advice:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。
关于切面表达式可参考博客spring aop切面表达式详解及例子
二、五种通知的执行顺序
-
在方法执行正常时:
- 环绕通知
@Around
- 前置通知
@Before
- 方法执行
- 环绕通知
@Around
- 后置通知
@After
- 正常返回通知
@AfterReturning
- 环绕通知
-
在方法执行抛出异常时:
这个时候顺序会根据环绕通知的不同而发生变化:
- 环绕通知捕获异常并不抛出:
- 环绕通知
@Around
- 前置通知
@Before
- 方法执行
- 环绕通知
@Around
- 后置通知
@After
- 正常返回通知
@AfterReturning
- 环绕通知
- 环绕通知捕获异常并抛出:
- 环绕通知
@Around
- 前置通知
@Before
- 方法执行
- 环绕通知
@Around
- 后置通知
@After
- 异常返回通知
@AfterThrowing
- 环绕通知
- 环绕通知捕获异常并不抛出:
三、代码示例
-
创建服务
-
创建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"); } }
-
-
创建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; // } }
-
-
创建测试用例
@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之间的执行顺序
- 使用注解@Order
- 实现接口Ordered
五、结论
Spring AOP就像一个同心圆,要执行的方法为圆心,最外层的order最小,环绕、前置通知先执行,后置、返回通知后执行。
网友评论