“This is the fourth day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

AOP (Aspect Orient Programming), commonly referred to as aspect-oriented Programming, is used as a complement to object orientation to address cross-cutting concerns that are distributed across modules in a system, such as transaction management, logging, caching, and so on. The key of AOP implementation lies in the AOP proxy automatically created by AOP framework. AOP proxy is mainly divided into static proxy and dynamic proxy. The static proxy is AspectJ. Dynamic proxies are represented by Spring AOP. This article will analyze and introduce AspectJ and Spring AOP implementations respectively.

Implement AOP using AspectJ’s compile-time enhancements

Let’s take an example. First we have a normal Hello class

public class Hello { public void sayHello() { System.out.println(“hello”); }

public static void main(String[] args) {
    Hello h = new Hello();
    h.sayHello();
}
Copy the code

}

Write an Aspect using AspectJ

public aspect TxAspect {
    void around(a):call(void Hello.sayHello()){
        System.out.println("Start business...");
        proceed();
        System.out.println("End of business..."); }}Copy the code

This simulates a transaction scenario, similar to Spring’s declarative transactions. Compile using AspectJ’s compiler, run the Hello class after compiling, and see the following output:

Start the transaction... Hello transaction end...Copy the code

Obviously, AOP has worked, so how exactly did AspectJ add new functionality to the Hello class without modifying it?

Take a look at the compiled Hello.class

public class Hello {
    public Hello(a) {}public void sayHello(a) {
        System.out.println("hello");
    }
 
    public static void main(String[] args) {
        Hello h = new Hello();
        sayHello_aroundBody1$advice(h, TxAspect.aspectOf(), (AroundClosure)null); }}Copy the code

Using Spring AOP

Unlike AspectJ’s static proxies, Spring AOP uses dynamic proxies, which means that the AOP framework does not modify the bytecode, but instead temporarily generates an AOP object for a method in memory that contains all of the target object’s methods, enhanced at specific pointcuts. And calls back the methods of the original object.

There are two main types of dynamic proxies in Spring AOP, JDK dynamic proxies and CGLIB dynamic proxies. JDK dynamic proxies receive proxied classes through reflection and require that the proxied classes implement an interface. At the heart of JDK dynamic proxies are the InvocationHandler interface and Proxy class.

If the target class does not implement the interface, Spring AOP chooses to use CGLIB to dynamically proxy the target class. CGLIB (Code Generation Library) is a Code Generation Library that can dynamically generate subclasses of a class at runtime. Note that CGLIB is dynamically proxyed by inheritance, so if a class is marked as final, it cannot be dynamically proxyed using CGLIB.

To test this claim, take a simple test. First test the implementation of the interface.

Define an interface

public interface Person {
    String sayHello(String name);
}
Copy the code

The implementation class

@Component
public class Chinese implements Person {
      @Timer
      @Override
      public String sayHello(String name) {
          System.out.println("-- sayHello() --");
          return name + " hello, AOP";
      }

      public void eat(String food) {
          System.out.println("I am eating:"+ food); }}Copy the code

The @timer annotation here is a generic annotation that I define myself to mark the Pointcut.

@Aspect
@Component
public class AdviceTest {
 
    @Pointcut("@annotation(com.zzm.aop.Timer)")
    public void pointcut(a) {}@Before("pointcut()")
    public void before(a) {
        System.out.println("before"); }}Copy the code
@SpringBootApplication
@RestController
public class SpringBootDemoApplication {
 
    // You must use the Person interface for injection
    @Autowired
    private Person chinese;
 
    @RequestMapping("/test")
    public void test(a) {
        chinese.sayHello("listen");
        System.out.println(chinese.getClass());
    }
 
    public static void main(String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); }}Copy the code

The output

before
-- sayHello() --
class com.sun.proxy. $Proxy53
Copy the code

You can see that the type is com.sun.proxy.$Proxy53, which is the previously mentioned proxy class, so Spring AOP uses the JDK’s dynamic proxy here.

If the interface is not implemented, modify the Chinese class

@Component
public class Chinese {
 
    @Timer
// @Override
    public String sayHello(String name) {
        System.out.println("-- sayHello() --");
        return name + " hello, AOP";
    }
 
    public void eat(String food) {
        System.out.println("I am eating:"+ food); }}Copy the code
@SpringBootApplication
@RestController
public class SpringBootDemoApplication {
 
    // Use the Chinese class directly
    @Autowired
    private Chinese chinese;
 
    @RequestMapping("/test")
    public void test(a) {
        chinese.sayHello("listen");
        System.out.println(chinese.getClass());
    }
 
    public static void main(String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); }}Copy the code

Output:

before
-- sayHello() --
class 
com.zzm.aop.Chinese$$EnhancerBySpringCGLIB$$56b89168
Copy the code

You can see that the class is enhanced by CGLIB, the dynamic proxy. The CGLIB proxy here is the Spring AOP proxy. This class is called an AOP proxy, and the AOP proxy class dynamically weaves enhanced processing at the pointcut.

summary

While AspectJ enhances the target object at compile time, Spring AOP’s dynamic proxy is dynamically enhanced at run time, generating AOP proxy objects. The difference is that AOP proxy objects are generated at different times. AspectJ’s static proxy approach has better performance. But AspectJ requires specific compiler processing, whereas Spring AOP requires no specific compiler processing.