Spring AOP vs AspectJ

概述

这两种都是目前流行的实现面向切面编程(Aspect Oriented Programming)的框架,AOP基本概念和详细的比较可以参见:Comparing Spring AOP and AspectJ

  1. Spring AOP是基于代理方法的运行时织入,可以分为JDK代理和CGLIB代理,详情可以参见:动态代理模式
  2. AspectJ是借助特殊的编译器直接将切面织入生成的二进制代码中

比较

Spring AOP使用方便,但功能简单,只能实现方法执行时的织入,其有诸多限制,比如:

  1. JDK代理是基于接口的,所以要求被代理的类实现了接口
  2. CGLIB代理是基于类继承的,所以要求被代理的类和方法不能是static、final和private的
  3. 由于是用新生成的代理类去替换原来的类,所以,在被代理的类内部之间的方法调用是使用的原来的方法而不是新生成的代理类中的方法
  4. 仅可用于被Spring容器管理的类 AspectJ使用复杂,但功能强大,可以实现任意位置的织入,且没有上面的诸多限制

使用

下面讲一下如何使用AspectJ在Spring Boot工程中实现AOP: 首先,基本的配置如下: Intro to AspectJ 目前工程中使用的是post-compile weaving方式, 可以不需要引入aspectjweaver,同时通过weaveDirectories指定了被织入class文件位置。

1
2
3
<weaveDirectories>
    <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>

其次,要想利用AspectJ模式的事务还需要以下配置:

  1. 在工程入口类上指定事务管理器模式:
    1
    @EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
    
  2. 在工程依赖和plugin依赖中都引入spring-aspects: Spring @Transactional With AspectJ 这是因为被AspectJ织入的aspect(实现事务的代码)是二进制形式的,在spring-aspects中,而不是源代码形式的: Applying already compiled aspect JARs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
          <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.11</version>
                <configuration>
                    <showWeaveInfo>true</showWeaveInfo>
                    <source>1.8</source>
                    <target>1.8</target>
                    <complianceLevel>1.8</complianceLevel>
                    <Xlint>ignore</Xlint>
                    <forceAjcCompile>true</forceAjcCompile>
                    <sources/>
                    <weaveDirectories>
                        <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
                    </weaveDirectories>
                    <aspectLibraries>
                        <aspectLibrary>
                            <groupId>org.springframework</groupId>
                            <artifactId>spring-aspects</artifactId>
                        </aspectLibrary>
                    </aspectLibraries>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

验证

  1. 在编译出来的class文件中看到被插入的代码(intellij idea中需要Decompile and Attach插件):
    1
    2
    3
    4
    5
    6
    7
    8
     @Transactional(
         rollbackFor = {Exception.class}
     )
     private void insertLogistics(List<LogisticsInfo> logisticsInfoList) {
         AnnotationTransactionAspect var10000 = AnnotationTransactionAspect.aspectOf();
         Object[] var3 = new Object[]{this, logisticsInfoList};
         var10000.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(this, new LogisticsService$AjcClosure1(var3), ajc$tjp_0);
     }
    
  2. 通过MySQL的general log查看实际执行的mysql语句,看前后是否有set autocommit = 0 和 set autocommit = 1 mysql的全量(查询)日志general-log的开启和分析方法