Comparison and application of Spring AOP and AspectJ

1 Introduction

AOP, that is, aspect-oriented programming is a very common technology, especially in Java Web development. The most popular AOP frameworks are Spring AOP and AspectJ respectively.

2 Spring AOP vs AspectJ

Spring AOP is implemented based on Spring IoC, which solves most of the common needs, but it is not a complete AOP solution. For objects that are not managed by the Spring container, it has nothing to do. AspectJ aims to provide a complete AOP solution, so it will be more complicated.

2.1 Weaving method

The weaving methods of the two are very different, which is also their essential difference, and they implement agents in different ways.

AspectJ is woven in before running and is divided into three categories:

  • compile time weaving
  • weaving after compilation
  • weave on load

Therefore, support from the AspectJ compiler (ajc) is required.

Spring AOP is woven at runtime and mainly uses two technologies: JDK dynamic proxy and CGLIB proxy. Use JDK Proxy for interfaces, and CGLIB for inherited ones.

2.2 Joinpoints

Because of the difference in weaving methods, the Joinpoints supported by the two are also different. Methods like final and static methods cannot be changed through dynamic proxies, so Spring AOP cannot support them. But AspectJ weaves the actual code directly before running, so the function will be 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

2.3 Performance

Compilation and weaving are much faster than runtime weaving. Spring AOP uses proxy mode to create corresponding proxy classes at runtime, which is not as efficient as AspectJ.

3 Spring Boot uses AspectJ

Because AspectJ is more powerful and will be used more in projects, so here we only introduce its integration with Spring Boot.

3.1 Import dependencies

Introduce the following dependencies, and add Lombok and aspectj on the basis of Spring Boot:

<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 AOP object

In order to verify the function of AOP, we add a TestController, which has a method for processing Get requests, and calls private member methods and static methods at the same time:

@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:

@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 captured and proxied. Here, all methods under the controller are configured.

And @Before and @Around define some processing logic. @Before is to print the method name, and @Around is to make a timing.

Note: It is not necessary to configure @EnableAspectJAutoProxy.

3.4 maven plugin

Because it needs to weave code at compile time, it needs the support of maven plugin: GitHub – mojohaus/aspectj-maven-plugin

Just configure the pom.xml file.

Then execute the command to package:

mvn clean package

At this time, some weaving information will be displayed, roughly as follows:

[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))

If you see the above information, it means that the code has been successfully woven. For details, you can check the generated class file.

It can be seen that many codes are not written by us, but are woven into the generated code.

3.5 Implementation and Testing

After the compilation is successful, we execute the code. If it is executed through IDEA, there is no need to build before running, because the package has been built through maven. It may not be possible to weave through IDEA’s built-in compiler build. Or choose ajc as compiler. For details, please refer to: IDEA starts Springboot but AOP fails

Access is as follows:

GET http://localhost:8080/test/hello

The log is as follows, and the AOP function is successfully implemented:

3.6 Some errors encountered

An error was encountered:

ajc Syntax error, annotations are only available if source level is 1.5 or greater

Plugins need to be configured:

<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>

You may also encounter an error that Lombok cannot be recognized. The configuration is as follows to solve this problem:

<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/>
    <weave Directories>
      <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
    </weaveDirectories>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

4 Summary

There are many AOP scene applications, and it still needs to be mastered.

Please see GitHub for the code: GitHub – LarryDpk/pkslow-samples: samples for www.pkslow.com: Java, Spring Boot, Spring Cloud, Docker, Kubernetes, Cloud, Big Data

Tags: Java