aspectj中call和execute在spring架构中的重要区别

在使用aspectj进行定义pointcut时,经常碰到的问题就是该使用call还是execute。当然,在spring中,只支持execute,而不支持call,但使用aspectj如ltw时就可以使用,但两者究竟有什么区别,最大的区别在哪儿,适用点又在哪儿。这就要从定义出来,来了解相关信息了。

在文档《aspectj_in_action_second_editon.pdf》中,针对二者,主要如下面所述:

a call is on the caller side, whereas execution happens on the receiver side—they’re two completely different places. 

即call工作在调用端,而execute工作在被调用端。
如我们将在pointcut打印一句helloworld。那么针对helloAction调用helloService而言。针对call调用,这句打印将会在helloAction中调用,而execute调用,而是在helloService中调用。

以上只是表面上的区别,在具体使用时其实并不是太大的差别,但如果有以下一句话,那么区别就可能完全不一样了:

aspectj是通过修改字节码来完成相应的功能,通过在相应的pointcut定义指定的advice来完成功能。

具体点说,就是aspectj首先需要能够匹配到相应的pointcut,然后才能执行相应的advice方法,如果不能匹配到pointcut,那么就不能执行相应的方法。

那么,考虑以下的一个pointcut表达式,在类AnnotationTransactionAspect中定义(spring-aspect.jar包)

execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *) || execution(@Transactional * *(..))

即执行相应在类上有Transactional注解或方法上有相应注释的方法时,追加事务支持。那么,如果我们把execute改为call会怎样,如下所示:

call(public * ((@Transactional *)+).*(..)) || call(@Transactional * *(..))

结果,可能就和预期完全不一样了。实际上,后面的pointcut匹配不了任何点。这与我们对service的使用有关,我们都习惯了使用注入,即在action中,通常是以下面的方式使用的:

@Resource
private HelloService helloService

public void execute() {
helloService.sayHi();
}

如上代码所示,我们这里使用的是接口,然后spring会自动注入实现类,相应的实现类为HelloServiceImpl。就是这里的代码,揭示了重要的区别,就是call和execute都是静态分析代码并进行weaving的,在整个接口HelloService中,即没有Transactional注解(相应的注解在实现类上),相应的方法也没有注解(注解也是在方法上),所以这里的调用并不会匹配pointcut。
但是,针对execute就不一样,实际的调用中,即会调用到helloServiceImpl,在调用内部的方法中,相应的方法则能够匹配execute的pointcut。

更形象的区别如下:

  • 在使用call时,aspectj会分析所有service的调用点,此时就会查看是否满足条件,然后会在调用点处追加代码(如before advice)。
  • 在使用execute时,aspectj不会分析调用点,而是直接在相应满足条件的方法的第一行追加代码(如before advice),因为只要是调用方法,自然就会匹配了,而如果不调用也没关系(相当于追加的代码没有用处)

因为在上面的call时,相应的方法类为Service接口,其不满足相应的Transactional注解条件,而在execute时,只要执行了ServiceImpl方法(不管什么时候或由哪个类调用),自然就满足条件,因为方法本身就满足相应的注解条件。

那么,既然call在某些场景并不能如execute方便,那么哪些场景有用呢,以下列出一个重要场景:

需要限制调用方法所在的类时,如只针对Action中调用service追加事务,在其它地方(如service内部或之间)则不追加,就可以使用within(Action)来过滤,而execute而不行,其既然在winthin(Service)内部

此场景,可避免无谓的创建事务,调用事务方法等,如service之间调用,常规的propagation下,不需要嵌套事务下,根本不需要为内部的service调用,创建事务属性等,这样可提高一定的执行效率。
另一方法,可避免重要计算一些serivce之间方法调用,在service之间调用的方法的统计就可以忽略,以精确了解每个方法所执行的一些监控信息等。

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201305130001.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

邮箱地址不会被公开。 必填项已用*标注