1. Introduction
AOP, or aspect-oriented programming is a very common technique, especially in Java Web development. And the most popular AOP frameworks are Spring AOP and AspectJ respectively.
2. Spring AOP vs AspectJ
Spring AOP is based on the Spring IoC implementation, which addresses most common requirements, but it is not a complete AOP solution. It has even less to offer for objects that are not managed by the Spring container. AspectJ, on the other hand, aims to provide a complete AOP solution and is therefore more complex.
2.1 Weave-in method
The two weaving methods are extremely different, and this is the essential difference between them, they implement proxies in different ways.
AspectJ is woven in before runtime and is divided into three categories.
- compile-time weaving
- post-compile weaving
- Load-time weaving
So it requires the support of AspectJ compiler (ajc).
Spring AOP is runtime weaving and uses two main techniques: JDK Dynamic Proxy and CGLIB Proxy. For interfaces JDK Proxy is used, while CGLIB is used for inheritance.
2.2 Joinpoints
Because of the difference in the way weaving is done, the supported Joinpoints are also different. Methods like final and static methods can’t be changed by dynamic proxies, so Spring AOP can’t support them. But AspectJ weaves in the actual code directly before running, so it is much more powerful.
Joinpoint |
Spring AOP Supported |
AspectJ Supported |
Method Call |
No |
Yes |
Method Execution |
Yes |
Yes |
Constructor Call |
No |
Yes |
Constructor Execution |
No |
Yes |
Static initializer execution |
No |
Yes |
Object initialization |
No |
Yes |
Field reference |
No |
Yes |
Field assignment |
No |
Yes |
Handler execution |
No |
Yes |
Advice execution |
No |
Yes |
Compile weave is much faster than runtime weave, Spring AOP uses the proxy pattern to create the corresponding proxy class at runtime, which is not as efficient as AspectJ.
3. Using AspectJ in Spring Boot
Because AspectJ is more powerful and will be used more often in projects, only its integration with Spring Boot is described here.
3.1 Adding dependencies
Import the following dependencies, with Lombok and aspectj added on top of Spring Boot.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
|
3.2 Objects proxied by AOP aspects
To verify the functionality of AOP, we add a TestController that has a method to handle Get requests and also calls private member methods and static methods.
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
|
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@GetMapping("/hello")
public String hello() {
log.info("------hello() start---");
test();
staticTest();
log.info("------hello() end---");
return "Hello, pkslow.";
}
private void test() {
log.info("------test() start---");
log.info("test");
log.info("------test() end---");
}
private static void staticTest() {
log.info("------staticTest() start---");
log.info("staticTest");
log.info("------staticTest() end---");
}
}
|
3.3 Configuring Aspect
The configuration aspects are as follows.
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
|
@Aspect
@Component
@Slf4j
//@EnableAspectJAutoProxy
public class ControllerAspect {
@Pointcut("execution(* com.pkslow.springboot.controller..*.*(..))")
private void testControllerPointcut() {
}
@Before("testControllerPointcut()")
public void doBefore(JoinPoint joinPoint){
log.info("------pkslow aop doBefore start------");
String method = joinPoint.getSignature().getName();
String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
log.info("Method: {}.{}" ,declaringTypeName, method);
log.info("------pkslow aop doBefore end------");
}
@Around("testControllerPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("------pkslow aop doAround start------");
long start = System.nanoTime();
Object obj = joinPoint.proceed();
long end = System.nanoTime();
log.info("Execution Time: " + (end - start) + " ns");
log.info("------pkslow aop doAround end------");
return obj;
}
}
|
@Pointcut
defines which classes and methods will be proxied, here all the methods under controller are configured.
And @Before
and @Around
define some processing logic. @Before
prints the method name, while @Around
does a timing.
Note: It is not necessary to configure @EnableAspectJAutoProxy
.
3.4 maven plugins
Because it is necessary to weave in the code at compile time, so you need the support of maven plugin: https://github.com/mojohaus/aspectj-maven-plugin
Just configure the pom.xml file.
Then execute the command to package.
This will display some woven-in information on the console, roughly as follows.
1
2
3
4
5
6
|
[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
[INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
|
When you see the above information, it means that the code was successfully woven in, and you can check the generated class file for details.
You can see that there is a lot of code that we did not write, but generated by weaving in.
3.5 Execution and Testing
After successful compilation, we execute the code. If you are executing through IDEA, you don’t need to build again before running, because the package has already been built through maven. Build through IDEA’s own compiler, it may not weave in. Or choose ajc as the compiler.
Testing.
1
|
GET http://localhost:8080/test/hello
|
The log output from the console is as follows, successfully implementing the AOP function.
3.6 Some of the errors encountered
1
|
ajc Syntax error, annotations are only available if source level is 1.5 or greater
|
The following plugin needs to be configured.
1
2
3
|
<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>
|
You may also encounter the error that Lombok is not recognized, configure the following to solve the problem.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</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>
|
The complete code can be found here.
Reference: https://www.pkslow.com/archives/spring-aop-vs-aspectj