背景

spring aop 平时的应用范围非常广泛,但是对于 private 和 static 的支持并不友好。

因为本质原理是通过 CGLIB 实现一个继承子类。

Spring AOP vs AspectJ

Spring AOP是基于Spring IoC实现的,它解决大部分常见的需求,但它并不是一个完整的AOP解决方案。

对于非Spring容器管理的对象,它更没有办法了。而AspectJ旨在提供完整的AOP方案,因此也会更复杂。

2.1 织入方式

两者织入方式有极大的不同,这也是它们的本质区别,它们实现代理的方式不同。

AspectJ是在运行前织入的,分为三类:

  • 编译时织入

  • 编译后织入

  • 加载时织入

因此需要AspectJ编译器(ajc)的支持。

而Spring AOP是运行时织入的,主要使用了两种技术:JDK动态代理和CGLIB代理。对于接口使用JDK Proxy,而继承的使用CGLIB。

2.2 Joinpoints

因为织入方式的区别,两者所支持的Joinpoint也是不同的。

像final的方法和静态方法,无法通过动态代理来改变,所以Spring AOP无法支持。但AspectJ是直接在运行前织入实际的代码,所以功能会强大很多。

2.3 性能

编译织入会比较运行时织入快很多,Spring AOP是使用代理模式在运行时才创建对应的代理类,效率没有AspectJ高。

Aspectj 入门例子

maven 引入

  [xml]
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springboot-aspectj-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!--spring-boot--> <spring-boot.version>1.3.5.RELEASE</spring-boot.version> <aspectj.version>1.9.7</aspectj.version> </properties> <dependencies> <!--spring-boot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring-boot.version}</version> </dependency> <!--aspectj--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> </plugin> <!--aspectj--> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>1.8</source> <target>1.8</target> <proc>none</proc> <showWeaveInfo>true</showWeaveInfo> <forceAjcCompile>true</forceAjcCompile> <sources/> <weaveDirectories> <weaveDirectory>${project.build.directory}/classes</weaveDirectory> </weaveDirectories> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>

测试 controller

  [java]
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
32
package org.example.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * localhost:8080/test/hello */ @RestController @RequestMapping("/test") public class TestController { @RequestMapping("/hello") public String hello() { System.out.println("---hello start---"); privateRest(); staticTest(); System.out.println("---hello end---"); return "Hello"; } private void privateRest() { System.out.println("---privateRest start---"); System.out.println("---privateRest start---"); } public static void staticTest() { System.out.println("---staticTest start---"); System.out.println("---staticTest start---"); } }

切面 aspect

  [java]
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
32
package org.example.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; @Aspect @Component //@EnableAspectJAutoProxy public class ControllerAspect { @Pointcut("execution(* org.example.controller..*.*(..))") private void testControllerPointcut() { } @Around("testControllerPointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("------------------------- around start ------------------------ "); long start = System.nanoTime(); Object obj = joinPoint.proceed(); long end = System.nanoTime(); System.out.println("------------------------- cost " + (end-start) + "ns"); System.out.println("------------------------- around end ------------------------ "); return obj; } }

测试

1) 首先进行编译

  [plaintext]
1
mvn clean package

此时,生成的 class 文件会进行增强。

查看 class 文件,效果如下:

  [java]
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
32
33
34
35
36
37
38
39
40
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.example.controller; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.runtime.reflect.Factory; import org.example.aspect.ControllerAspect; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping({"/test"}) public class TestController { public TestController() { } @RequestMapping({"/hello"}) public String hello() { JoinPoint var1 = Factory.makeJP(ajc$tjp_0, this, this); return (String)hello_aroundBody1$advice(this, var1, ControllerAspect.aspectOf(), (ProceedingJoinPoint)var1); } private void privateRest() { JoinPoint var1 = Factory.makeJP(ajc$tjp_1, this, this); privateRest_aroundBody3$advice(this, var1, ControllerAspect.aspectOf(), (ProceedingJoinPoint)var1); } public static void staticTest() { JoinPoint var0 = Factory.makeJP(ajc$tjp_2, (Object)null, (Object)null); staticTest_aroundBody5$advice(var0, ControllerAspect.aspectOf(), (ProceedingJoinPoint)var0); } static { ajc$preClinit(); } }

2)页面访问

http://localhost:8080/test/hello

对应的日志效果:

  [plaintext]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
------------------------- around start ------------------------ ---hello start--- ------------------------- around start ------------------------ ---privateRest start--- ---privateRest start--- ------------------------- cost 49000ns ------------------------- around end ------------------------ ------------------------- around start ------------------------ ---staticTest start--- ---staticTest start--- ------------------------- cost 34000ns ------------------------- around end ------------------------ ---hello end--- ------------------------- cost 334600ns ------------------------- around end ------------------------

参考资料

简易版的Spring框架之AOP简单实现(对我来说不简单啊)

spring-AOP(二)实现原理之AspectJ注解方式

Spring5参考指南:AspectJ注解

Spring AOP之@AspectJ注解

Spring AOP与AspectJ的对比及应用