执行流程 | 你真的了解Spring AOP的执行顺序吗?

原创
10/18 03:19
阅读数 14



Hi! 我是小小,我们又见面了,今天的主要内容是,你真的了解Spring AOP的执行顺序吗?跟随着我的脚步,一块丈量世界,了解世界,重新认识,重新了解Spring AOP的执行顺序。

聊一聊毕业四个月的感受

毕业四个月了,劳动合同还没有签,一切都没有稳定下来,不过也似乎也将要稳定下来了,生活如流水一般非常平稳的运行着,而我的公众号也在非常平稳的编写着,当然啦,自己最喜欢听的音乐也还是没有变,MySoul,一成不变,似乎生活进入了稳定状态,又或者生活没有进入稳定状态,我也不知道,只知道,这就是生活,唯有这样的生活才能继续进行。这四个月对我感觉,就是每天敲啊敲,每天的敲,拿着微薄的薪水,获得着微薄的收入,或许这就是生活,自己的,也许这就是一个普通人,再也不能普通过的了生活了,好啦,不吐槽了,生活还需要继续进行下去,我们隆重的开始今天的正文。

AOP核心概念

需要了解AOP,首先需要了解这些Spring AOP这些核心概念。

  1. Aspect 切面:由一系列切点,增强和引入组成的模块对象,可定义优先级,从而影响增强和引入的执行顺序,事物管理,在Java中就是一个切面应用的例子。
  2. Join point 接入点:程序执行期的一个点,例如方法执行,类初始化,异常处理,在Spring AOP中,接入点始终表示方法的执行。
  3. Advice 增强切面在特点接入点的执行动作,包括 around,before,and after等多种类型,包含Spring在内的许多AOP框架,通常会使用拦截器来实现增强,围绕着接入点维护一个拦截器链。
  4. Pointcut 切点,用来匹配特定接入点的表达式,增强将会与切点表达式产生关联,并运行在任何切点匹配到的接入点上,通过切点表达式匹配接入点是AOP的核心,Spring默认使用Aspect的切点表达式。
  5. Introduction 引入:为某个type声明额外的方法和字段,Spring AOP允许你引入任何接口以及它的默认实现到被挣钱对象上。
  6. Target Object 目标对象,被一个或多个切面增强的对象,也被叫做被增强对象,既然Spring AOP使用运行时代理,那么目标对象就是代理对象。
  7. AOP proxy AOP代理,为了实现切面功能一个对象会被AOP框架创建出来,在Spring框架中,AOP代理的默认方式是,有接口,就使用基于接口的jdk动态代理,否则使用基于类的CGLIB动态代理,但是我们可以通过设置 proxy-target-class="true"完全使用CGLIB动态代理。
  8. Weaving 织入,把一个或多个切面与类或对象链接在一起创建一个被增强对象,织入能发生在编译时,加载时,或运行时,Spring AOP默认就是运行时织入,可以通过枚举adviceMode来设置。

模拟aspect advice的执行过程

在这里我们不再展示测试代码,而是通过简单的代码来模拟aspect advice的执行过程。

话不多说,直接上代码

package doubt;
public class AspectAdviceInvokeProcess {
public static void main(String[] args){
try {
//正常执行
AspectInvokeProcess(false);
System.out.println("=====分割线=====");
//异常执行
AspectInvokeProcess(true);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 切面执行过程
* @param isException
* @throws Exception
*/
public static void AspectInvokeProcess(boolean isException) throws Exception{
try {
try {
aroundAdvice(isException);
} finally {
afterAdvice();
}
afterReturningAdvice();
return;
} catch (Exception e) {
afterThrowingAdvice(e);
throw e;
return;
}
}

/**
* 环绕增强
* @param isException
* @throws Exception
*/
private static void aroundAdvice(boolean isException) throws Exception {
System.out.println("around before advice");
try {
JoinPoint_Proceed(isException);
} finally {
System.out.println("around after advice");
}
}

/**
* 编织后的接入点执行过程
* @param isException
*/
public static void JoinPoint_Proceed(boolean isException){
beforeAdvice();
targetMethod(isException);
}

/**
* 前置增强
*/
private static void beforeAdvice() {
System.out.println("before advice");
}

/**
* 目标方法
* @param isException
*/
private static void targetMethod(boolean isException) {
System.out.println("target method 执行");
if(isException)
throw new RuntimeException("异常发生");
}

/**
* 后置增强
*/
private static void afterAdvice() {
System.out.println("after advice");
}

/**
* 正常返回增强
*/
private static void afterReturningAdvice() {
System.out.println("afterReturning");
}

/**
* 异常返回增强
* @param e
* @throws Exception
*/
private static void afterThrowingAdvice(Exception e) throws Exception {
System.out.println("afterThrowing:"+e.getMessage());
}
}

执行结果

上面代码的执行结果如下,直接体现了同一aspect,不同advice的执行顺序。

around before advice
before advice
target method 执行
around after advice
after advice
afterReturning
===============分割线==============
around before advice
before advice
target method 执行
around after advice
after advice
afterThrowing:异常发生
java.lang.RuntimeException: 异常发生

结果图

不同aspect,advice的执行顺序

Spring AOP 通过制定 aspect 的优先级,控制不同的 aspect,advice的执行顺序。

  1. Aspect 类添加注解:org.springframework.core.annotation.Order,使用注解value属性指定优先级。
  2. Aspect 类实现接口:org.springframework.core.Ordered,实现 Ordered 接口的 getOrder() 方法。

其中,数值越低,表明优先级越高,@Order 默认为最低优先级,即最大数值:

    /**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

最终,不同aspect,advice的执行顺序。

  1. 入操作,接入点执行前,Brfore 优先级越高,越先执行。
  2. 一个切面的入操作执行完,才到下一切面,所有切面入操作执行完,才开始执行接入点。
  3. 出操作,优先级越低,越先执行。
  4. 一个切面的出操作执行完,才到下一个切面,知道返回调用点。

图示

同一切面,相同接入点

其执行顺序不能直接确定,有如下两种变通方式

  1. 将两个 advice 合并为一个 advice,那么执行顺序就可以通过代码控制了
  2. 将两个 advice 分别抽离到各自的 aspect 内,然后为 aspect 指定执行顺序

Spring 事物管理器

Spring 事物管理器是基于Spring AOP的

切面优先级

切面优先级,默认为最低的优先级

LOWEST_PRECEDENCE = Integer.MAX_VALUE

关于作者

我是小小,一枚生在二线,活在一线城市的程序猿,一直致力于奋斗在一线编码,我是小小,我们下期再见。

小明菜市场

推荐阅读

● 吊打面试官 | Java到底是值传递还是引用传递

● 容器 | Docker 如此之好,你为什么还要用k8s

● 分布式ID | 这六种分布式ID生成方法,总有一款适合你

● 数据结构与算法 | 来来来,让我们重新认识一下什么是树

● 面试官 | Java转List三种方式,你说说吧。我。。懵逼。啥时候有三种了

给我个好看再走好吗?


本文分享自微信公众号 - 小明菜市场(fileGeek)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部