spring学习笔记(12)@AspectJ研磨分析[3]增强织入顺序实例详解

增强的织入顺序

当一个连接点同时织入多个增强时,就存在顺序调用问题:
1. 增强在同一个切面类中定义:依照增强在切面类中定义的顺序依次织入。
2. 增强位于不同的切面,但果这些切面都实现了org.springframework.core.Ordered
接口,则由接口注解的顺序号决定,顺序号越小,对于不同的增强,织入顺序为:
1. 前置增强->越先织入
2. 后置增强->越后织入
3. 最终增强->越后织入
4. 环绕增强->调用原方法之前的部分先织入,调用原方法之后的部分后织入
我们先来看一个实例:

1.目标对象

package test.aop2;

public class UserController {
    public void login(String name){
        System.out.println("I'm "+name+" ,I'm logining");
    }
}

2. 切面配置

/*-----------------------有序切面类1--------------------------*/
@Aspect
public class Annotation_aspect implements Ordered {//实现顺序接口

    @Pointcut("target(test.aop2.UserController)")
    private void pointcut(){}//定义切点

    @AfterThrowing("pointcut()")//后置增强
    public void AfterThrowing() throws Throwable{
        System.out.println("Annotation_aspect 实施AfterThrowing,优先级为" + getOrder());
    }
    @After("pointcut()")//最终增强
    public void after() throws Throwable{
        System.out.println("Annotation_aspect 实施after,优先级为" + getOrder());
    }
    @Before("pointcut()")//前置增强
    public void before() throws Throwable{
        System.out.println("Annotation_aspect 实施@before,优先级为" + getOrder());
    }
    @Around("pointcut()")//环绕增强
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("Annotation_aspect 实施around前,优先级为" + getOrder());
        joinPoint.proceed();
        System.out.println("Annotation_aspect 实施around后,优先级为" + getOrder());
    }

    @Override
    public int getOrder() {
        return 1;
    }

}
/*-----------------------有序切面类2--------------------------*/
@Aspect
public class Annotation_aspect2 implements Ordered {

    @Pointcut("target(test.aop2.UserController)")
    private void pointcut(){}

    @AfterThrowing("pointcut()")
    public void AfterThrowing() throws Throwable{
        System.out.println("Annotation_aspect2 实施AfterThrowing,优先级为" + getOrder());
    }
    @After("pointcut()")
    public void after() throws Throwable{
        System.out.println("Annotation_aspect2 实施after,优先级为" + getOrder());
    }
    @Before("pointcut()")
    public void before() throws Throwable{
        System.out.println("Annotation_aspect2 实施@before,优先级为" + getOrder());
    }
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("Annotation_aspect2 实施around前,优先级为" + getOrder());
        joinPoint.proceed();
        System.out.println("Annotation_aspect2 实施around后,优先级为" + getOrder());
    }

    @Override
    public int getOrder() {
        return 2;
    }

}
/*-----------------------无序切面类1--------------------------*/
@Aspect
public class noOrder_aspect {

    @Pointcut("target(test.aop2.UserController)")
    private void pointcut(){}

    @AfterThrowing("pointcut()")
    public void AfterThrowing() throws Throwable{
        System.out.println("noOrder_aspect 实施AfterThrowing,无优先级");
    }
    @After("pointcut()")
    public void after() throws Throwable{
        System.out.println("noOrder_aspect 实施after,无优先级");
    }
    @Before("pointcut()")
    public void before() throws Throwable{
        System.out.println("noOrder_aspect 实施@before,无优先级");
    }
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("noOrder_aspect 实施around前,无优先级");
        joinPoint.proceed();
        System.out.println("noOrder_aspect 实施around后,无优先级");
    }
}
/*-----------------------无序切面类2--------------------------*/
@Aspect
public class noOrder_aspect2 {

    @Pointcut("target(test.aop2.UserController)")
    private void pointcut(){}

    @AfterThrowing("pointcut()")
    public void AfterThrowing() throws Throwable{
        System.out.println("noOrder_aspect2 实施AfterThrowing,无优先级");
    }
    @After("pointcut()")
    public void after() throws Throwable{
        System.out.println("noOrder_aspect2 实施after,无优先级");
    }
    @Before("pointcut()")
    public void before() throws Throwable{
        System.out.println("noOrder_aspect2 实施@before,无优先级");
    }
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("noOrder_aspect1 实施around前,无优先级");
        joinPoint.proceed();
        System.out.println("noOrder_aspect1 实施around后,无优先级");
    }

}

3. IOC容器配置

<aop:aspectj-autoproxy />   <!-- 使@AspectJ注解生效 -->
<bean class="test.aop2.noOrder_aspect" />
<bean class="test.aop2.noOrder_aspect2" />
<bean class="test.aop2.Annotation_aspect2" />
<bean class="test.aop2.Annotation_aspect" />
<bean id="userController" class="test.aop2.UserController" /><!-- 注册目标对象 -->

4. 测试方法

public static void main(String args[]) throws Exception{
    ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:test/aop2/aop.xml");
    UserController userController = (UserController) ac.getBean("userController");
    User user = new User();
    userController.login("zenghao");
}

运行测试方法,控制台输出:

Annotation_aspect 实施around前,优先级为1
Annotation_aspect 实施@before,优先级为1
Annotation_aspect2 实施around前,优先级为2
Annotation_aspect2 实施@before,优先级为2
noOrder_aspect 实施around前,无优先级
noOrder_aspect 实施@before,无优先级
noOrder_aspect1 实施around前,无优先级
noOrder_aspect2 实施@before,无优先级
I’m zenghao ,I’m logining
noOrder_aspect1 实施around后,无优先级
noOrder_aspect2 实施after,无优先级
noOrder_aspect2 实施AfterReturning,无优先级
noOrder_aspect 实施around后,无优先级
noOrder_aspect 实施after,无优先级
noOrder_aspect 实施AfterReturning,无优先级
Annotation_aspect2 实施around后,优先级为2
Annotation_aspect2 实施after,优先级为2
Annotation_aspect2 实施AfterReturning,优先级为2
Annotation_aspect 实施around后,优先级为1
Annotation_aspect 实施after,优先级为1
Annotation_aspect 实施AfterReturning,优先级为1

针对以上信息,我们再复述前面结论来对比看,是完全符合的,顺序号越小,对于不同的增强,织入顺序为:
1. 前置增强->越先织入
2. 后置增强->越后织入
3. 最终增强->越后织入
4. 环绕增强->调用原方法之前的部分先织入,调用原方法之后的部分后织入

认真观察,我们还会发现:
1. 同一切面中,不同增强的织入顺序为:环绕增强前半部分->前置增强->原目标函数调用->环绕增强后半部分->最终增强->后置增强。
2. 没有实现order接口的“优先级”总是低于实现了order接口的,如针对前置增强来看,顺序为:
Annotation_aspect > Annotation_aspect2 > noOrder_aspect > noOrder_aspect
3. 先观察无序接口,我们会发现noOrder_aspect的优先级总是高于noOrder_aspect2的优先级,这是有前面IOC容器中前面Bean的配置顺序决定的:

<bean class="test.aop2.noOrder_aspect" />
<bean class="test.aop2.noOrder_aspect2" />
<bean class="test.aop2.Annotation_aspect2" />
<bean class="test.aop2.Annotation_aspect" />

那么,对于同样实现了order接口的切面,它们是否也满足这个规律呢?我们把Annotation_aspect2的优先级也设为1,然后运行测试函数,打印信息如下:
Annotation_aspect2 实施around前,优先级为1
Annotation_aspect2 实施@before,优先级为1
Annotation_aspect 实施around前,优先级为1
Annotation_aspect 实施@before,优先级为1
noOrder_aspect 实施around前,无优先级
noOrder_aspect 实施@before,无优先级
noOrder_aspect1 实施around前,无优先级
noOrder_aspect2 实施@before,无优先级
I’m zenghao ,I’m logining
noOrder_aspect1 实施around后,无优先级
noOrder_aspect2 实施after,无优先级
noOrder_aspect2 实施AfterReturning,无优先级
noOrder_aspect 实施around后,无优先级
noOrder_aspect 实施after,无优先级
noOrder_aspect 实施AfterReturning,无优先级
Annotation_aspect 实施around后,优先级为1
Annotation_aspect 实施after,优先级为1
Annotation_aspect 实施AfterReturning,优先级为1
Annotation_aspect2 实施around后,优先级为1
Annotation_aspect2 实施after,优先级为1
Annotation_aspect2 实施AfterReturning,优先级为1

关注粗体部分,是不是优先级和IOC容器的配置顺序对应上了,感兴趣的同学可以把两个没有实现order接口的配置顺序也交换一下,观察它们的织入优先级是否也跟着变化了?

除了实现org.springframework.core.Ordered配置顺序外,我们还可以使用注解@org.springframework.core.annotation.Order(优先级整数)来实现同样的效果。在这里,如果既使用注解,又使用接口实现的,且它们的优先级一致时,一样是看IOC容器的bean顺序,如:

<!-- <bean class="test.aop2.noOrder_aspect" />
<bean class="test.aop2.noOrder_aspect2" /> -->
<bean class="test.aop2.Annotation_aspect3" />
<bean class="test.aop2.Annotation_aspect4" />
<bean class="test.aop2.Annotation_aspect2" />
<bean class="test.aop2.Annotation_aspect" />

其中Annotation_aspect3和Annotation_aspect4的类定义头部为:

@Aspect
@Order(1)
public class Annotation_aspect3{
....
}
@Aspect
@Order(2)
public class Annotation_aspect4{
....
}

注释再次运行测试程序,得到打印信息:

Annotation_aspect3 实施around前,注解优先级为1
Annotation_aspect3 实施@before,注解优先级为1
Annotation_aspect 实施around前,优先级为1
Annotation_aspect 实施@before,优先级为1
Annotation_aspect4 实施around前,注解优先级为2
Annotation_aspect4 实施@before,注解优先级为2
Annotation_aspect2 实施around前,优先级为2
Annotation_aspect2 实施@before,优先级为2
I’m zenghao ,I’m logining
Annotation_aspect2 实施around后,优先级为2
Annotation_aspect2 实施after,优先级为2
Annotation_aspect2 实施AfterReturning,优先级为2
Annotation_aspect4 实施around后,注解优先级为2
Annotation_aspect4 实施after,注解优先级为2
Annotation_aspect4 实施AfterReturning,注解优先级为2
Annotation_aspect 实施around后,优先级为1
Annotation_aspect 实施after,优先级为1
Annotation_aspect 实施AfterReturning,优先级为1
Annotation_aspect3 实施around后,注解优先级为1
Annotation_aspect3 实施after,注解优先级为1
Annotation_aspect3 实施AfterReturning,注解优先级为1
观察IOC容器的配置顺序,这里也是对应上的。
综上,我们得到结论:多个同一类型的增强(如都是前置增强)对同一连接点的织入顺序是:配置有优先级看谁优先级高,优先级相同则看谁先在IOC容器注册,配置了order接口或注释的增强优先级总是高于没配置的。

通过以上分析,我们能够较好地总结出我们常用增强对应各种情况的织入顺序。在了解这些织入顺序后,有助于我们更灵活地将AOP运用到我们的项目中。

源码下载

本博文提到的示例源码请到我的github仓库https://github.com/jeanhao/spring的aop分支下载。

时间: 2024-08-11 09:18:08

spring学习笔记(12)@AspectJ研磨分析[3]增强织入顺序实例详解的相关文章

spring学习笔记(10)@AspectJ研磨分析[1]入门、注解基本介绍

@AspectJ准备 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. 在使用AspectJ之前,我们需要导入aspectJ相应的jar包,可到我的资源页http://download.csdn.net/detail/qwe6112071/9468329 中下载,而如果使用maven则可直接在pom.xml中加入如下代码: <dependency> <groupId>o

spring学习笔记(11)@AspectJ研磨分析[2]切点表达式函数详解

Spring中的AspectJ切点表达式函数 切点表达式函数就像我们的GPS导航软件.通过切点表达式函数,再配合通配符和逻辑运算符的灵活运用,我们能很好定位到我们需要织入增强的连接点上.经过上面的铺垫,下面来看看Springz中支持的切点表达式函数. 1. 方法切点函数 函数 入参 说明 示例 execution() 方法匹配字符串 满足某一匹配模式的的所有目标类方法连接点 execution(* com.yc.service.*.*(..))在配置service层的事务管理时常用,定位于任意返

Android 学习笔记 Contacts ContentResolver query、add、update、delete 参数详解

1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME. public void fetchAllContacts() { ContentResolver contentResolver = this.getContentResolver(); Cursor cursor = contentResolver.query(android.provider.ContactsContract.Contacts.CONTENT_URI, null, null, null,

spring学习笔记(13)基于Schema配置AOP详解

基于Schema配置入门实例 除了基于@AspectJ注解的形式来实现AOP外,我们还可以在IOC容器中配置.先来看看一个常见的应用场景,在我们的web项目中,我们需要为service层配置事务,传统的做法是在每个业务逻辑方法重复下面配置中: Created with Raphaël 2.1.0程序开始1. 获取DAO层封装好的数据库查询API,如HIbernate中的SessionFactory/Session和mybatis中的xxxMapper2. 开启事务3. 根据入参查询数据库完成相应

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >

spring学习笔记(21)编程式事务配置,service层概念引入

访问数据库事务导入 在我之前的文章<spring学习笔记(19)mysql读写分离后端AOP控制实例>中模拟数据库读写分离的例子,在访问数据库时使用的方法是: public <E> E add(Object object) { return (E) getSessionFactory().openSession().save(object); } 通过直接开启session而后保存对象.查询数据等操作,是没有事务的.而如果我们的项目规模变大,业务逻辑日益复杂,我们在一个方法中进行大

Spring学习笔记之aop动态代理(3)

Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. PersonDao.java public interface PersonDao { public void savePerson(); } PersonDaoImpl.java public class PersonDaoImpl implements PersonDao{ public void save

spring学习笔记(16)趣谈spring 事件机制[2]:多监听器流水线式顺序处理

上一篇我们使用到的ApplicationListener是无序的,结合异步调度它能满足了我们的大部分应用场景,但现在我们来个另类的需求,我们来模拟一条作业调度流水线,它不能异步,必须按照先后次序执行不同的任务才能得到我们的最终结果. 需求示例:现在假如华中科技大学的小白想要为它的智能机器人作品申报国家创新奖,需要经过学校.省级创新科研机构.国家创新科研机构逐层审核.我们尝试通过事件来实现,核心就在监听器实现SmartApplicationListener接口.示例如下: 1. 配置事件发布者小白

Spring学习笔记之依赖的注解(2)

Spring学习笔记之依赖的注解(2) 1.0 注解,不能单独存在,是Java中的一种类型 1.1 写注解 1.2 注解反射 2.0 spring的注解 spring的 @Controller@Component@Service//更多典型化注解,但是@Controller@Service建议使用 @service("personService")可以代替set get 方法,@Resource(name=personDao) @Autowired//按照类型匹配 @Qualifier