In-depth analysis of the AOP principle of the Spring framework

Spring AOP (Aspect Oriented Programming) is a framework based on aspect-oriented programming, which can manage multiple target objects in a unified manner and add additional functions without modifying the original business code. The implementation process mainly relies on proxy (Proxy) and dynamic proxy (Dynamic Proxy) technologies. This article will analyze the implementation principle of Spring AOP in detail.

1. The basic concept of AOP

AOP helps us better define our business logic by decomposing an application into its important parts and then separating those concerns that span those parts. Among them, the core concepts include:

  • Aspect (Aspect) Aspect is the functional code woven into the target class. For Java, it can be the code at a specific point of a method, constructor or class. Aspects can include pre-notification, post-notification, surround notification, exception notification, and final notification, etc., which can be customized according to business needs.

  • Connection point (Join Point) The connection point indicates where the aspect will be woven into the target object. For Java, it is usually the execution point of a method or the creation point of a constructor.

  • Pointcut (Pointcut) Pointcut is a collection of connection points, which defines which connection points need to be woven into the target object.

  • Advice (Advice) Advice includes various types of aspect codes, such as pre-advice, post-advice, surround advice, exception advice, and final advice.

  • Weaving Weaving is the process of weaving the aspect code into the target object, which can happen at compile time, load time or runtime. Spring AOP uses dynamic proxy technology as the main method for weaving.

  • Introduction Introduction allows us to add new methods and properties to an existing class that are not defined in the original class definition but defined in the imported interface.

Two, three ways of AOP implementation

Before introducing the implementation principle of Spring AOP, we first understand the three ways to implement AOP: static proxy, using JDK's Proxy class to implement dynamic proxy, and using CGLIB to implement dynamic proxy.

In order to better understand the implementation principle of Spring AOP and the way AOP uses dynamic proxy, we can have a deep understanding of three ways: static proxy, JDK dynamic proxy and CGLIB dynamic proxy.

1. Static proxy

Static proxy refers to the classes and methods that need to be proxied have been determined at compile time. The proxy class already exists at compile time. Its advantage is high operating efficiency, but its disadvantage is that it is not flexible and can only proxy pre-defined classes and methods. The following is a simple example to illustrate the principle of static proxy:

First define an interface Subject:

public interface Subject {
    void request();
}

Then define a target object RealSubject:

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is handling the request.");
    }
}

Finally define a proxy object ProxySubject:

public class ProxySubject implements Subject {
    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("ProxySubject is handling the request.");
        realSubject.request();
    }
}

In the above example, ProxySubject is a proxy object, which implements the Subject interface, holds a real object realSubject, and adds additional processing logic in the request() method.

When we need to call a method in the Subject interface, we can call it by creating a proxy object ProxySubject, as follows:

public static void main(String[] args) {
    Subject subject = new RealSubject();
    subject.request();

    Subject proxySubject = new ProxySubject(subject);
    proxySubject.request();
}

When running the program, we will find the following output:

RealSubject is handling the request.
ProxySubject is handling the request.
RealSubject is handling the request.

In the above example, when the proxy object ProxySubject calls the request() method, it first prints the sentence "ProxySubject is handling the request.", and then calls the request() method of the target object RealSubject.

2. JDK dynamic proxy

JDK dynamic proxy refers to the process of dynamically generating proxy classes through the Proxy class and InvocationHandler interface that comes with Java. The proxy class dynamically generated at runtime can implement any interface. Compared with static proxy, it is more flexible, but it can only be implemented by proxy. A class that has an interface cannot proxy a class that does not implement an interface. The following is a simple example to illustrate the principle of JDK dynamic proxy:

First define an interface Subject:

public interface Subject {
    void request();
}

Then define a target object RealSubject:

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is handling the request.");
    }
}

Then define an InvocationHandler implementation class MyInvocationHandler:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("ProxySubject is handling the request.");
        Object result = method.invoke(target, args);
        return result;
    }
}

In the above example, MyInvocationHandler implements the InvocationHandler interface, and rewrites the invoke() method, adding additional processing logic to this method.

Finally, create a dynamic proxy object in the main program:

public static void main(String[] args) {
    Subject realSubject = new RealSubject();
    InvocationHandler invocationHandler = new MyInvocationHandler(realSubject);
    Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), invocationHandler);
    proxySubject.request();
}

When running the program, we will find the following output:

ProxySubject is handling the request.
RealSubject is handling the request.

In the above example, the proxy object proxySubject is created by calling the Proxy.newProxyInstance() method, which can proxy any class that implements the Subject interface.

3. CGLIB dynamic proxy

CGLIB dynamic proxy refers to the process of using CGLIB library to dynamically generate proxy classes. Unlike JDK dynamic proxy, CGLIB dynamic proxy can proxy classes that do not implement interfaces, and has wider applicability. The following is a simple example to illustrate the principle of CGLIB dynamic proxy:

First define a target object RealSubject:

public class RealSubject {
    public void request() {
        System.out.println("RealSubject is handling the request.");
    }
}

Then define a MethodInterceptor implementation class MyMethodInterceptor:

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("ProxySubject is handling the request.");
        Object result = proxy.invokeSuper(obj, args);
        return result;
    }
}

In the above example, MyMethodInterceptor implements the MethodInterceptor interface and rewrites the intercept() method, adding additional processing logic to this method.

Finally, create a dynamic proxy object in the main program:

public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(RealSubject.class);
    enhancer.setCallback(new MyMethodInterceptor());
    RealSubject proxySubject = (RealSubject) enhancer.create();
    proxySubject.request();
}

When running the program, we will find the following output:

ProxySubject is handling the request.
RealSubject is handling the request.

In the above example, the proxy object proxySubject is created by calling the Enhancer.create() method, which can proxy any class, including classes that do not implement any interface.

Generally speaking, static proxies and JDK dynamic proxies are more widely used in these three methods. When the requirements are relatively simple and only a small number of interfaces or classes need to be proxied, static proxies and JDK dynamic proxies can be selected; CGLIB dynamic proxies are relatively more flexible and can be The proxy does not implement the class of the interface, but because it uses the ASM bytecode operation library, it is slightly less efficient than the JDK dynamic proxy. Therefore, when choosing to implement AOP, it is necessary to choose a suitable implementation method according to specific needs .

Three, Spring AOP implementation principle

Spring AOP is mainly implemented through the standard API provided by the AOP Alliance. The core interfaces include:

  • Advisor Advisor is the basic class of aspect, which is associated with Join Point and Advice, and only needs to deal with Advisor when calling externally.

  • Pointcut Pointcut is used to describe which connection points in AOP should be woven into aspect code, and it can match connection points according to patterns called pointcut expressions.

  • Advice Advice is the specific logic of the aspect, including Before, AfterReturning, Around, AfterThrowing, and After.

  • JoinPoint JoinPoint represents a specific connection point of the target object, and information about the connection point can be obtained in Advice.

  • ProxyFactory ProxyFactory is a proxy factory, which is responsible for creating dynamic proxy objects and weaving aspects into target objects. At the same time, various decorators can be added to achieve different functions.

Below we use a simple example to explain in detail the implementation principle of Spring AOP.

First define a Person class:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void say() {
        System.out.println("Hello, my name is " + name + ", I'm " + age + " years old.");
    }
}

Then define an aspect class LogAspect:

public class LogAspect {
    public void before(JoinPoint joinPoint) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " is invoked before...");
    }

    public void after(JoinPoint joinPoint) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " is invoked after...");
    }
}

In the above example, LogAspect is an aspect class, which includes two notification methods before and after.

Define another test class:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = (Person) context.getBean("person");
        person.say();
    }
}

Finally configure in applicationContext.xml:

<bean id="person" class="com.example.Person">
    <constructor-arg value="Tom"></constructor-arg>
    <constructor-arg value="18"></constructor-arg>
</bean>

<bean id="logAspect" class="com.example.LogAspect"></bean>

<aop:config>
    <aop:aspect id="log" ref="logAspect">
        <aop:before method="before" pointcut="execution(* com.example.Person.say(..))"></aop:before>
        <aop:after method="after" pointcut="execution(* com.example.Person.say(..))"></aop:after>
    </aop:aspect>
</aop:config>

Among them, the aop:config tag is used to configure the AOP proxy, the aop:aspect tag is used to configure the aspect information, the pointcut attribute is used to specify the entry point, and the method attribute is used to specify the notification method.

When running the program, we will find that the following results are output:

Method say is invoked before...
Hello, my name is Tom, I'm 18 years old.
Method say is invoked after...

In the above example, the implementation principle of Spring AOP mainly includes the following steps:

  • Parse the bean definition according to the Spring configuration file, and create the corresponding Java Bean object.

  • When creating a Bean object, if the object is declared to be proxied, a dynamic proxy object will be created and returned according to all Advisors defined by the Bean.

  • In the process of creating a dynamic proxy object, Spring will create the bytecode of the proxy class through JDK's own or third-party libraries such as CGLIB and dynamically load it into the JVM memory.

  • Before calling the target method, the dynamic proxy object will call the corresponding aspect logic first, and then call the target method itself.

  • After the target method is executed, the dynamic proxy object will call the corresponding aspect logic again to complete the entire proxy process.

Four. Summary

Spring AOP is an aspect programming framework based on proxy and dynamic proxy technology, which improves code reusability and maintainability by decomposing business logic into multiple parts. In the implementation process, Spring AOP mainly manages and weaves aspect codes through core interfaces such as Advisor, Pointcut, Advice, JoinPoint, and ProxyFactory. When using Spring AOP, we need to define aspect classes, join points, and pointcuts, and specify various advice types through the aop:config and aop:aspect tags in the Spring configuration file.

 

Guess you like

Origin blog.csdn.net/xxxzzzqqq_/article/details/130640995