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: 异常发生

结果图

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

不同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. 一个切面的出操作执行完,才到下一个切面,知道返回调用点。

图示

执行流程 | 你真的了解Spring AOP的执行顺序吗?插图(1)

同一切面,相同接入点

其执行顺序不能直接确定,有如下两种变通方式
1. 将两个 advice 合并为一个 advice,那么执行顺序就可以通过代码控制了
2. 将两个 advice 分别抽离到各自的 aspect 内,然后为 aspect 指定执行顺序

Spring 事物管理器

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

切面优先级

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

LOWEST_PRECEDENCE = Integer.MAX_VALUE

关于作者

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