"Design Patterns" Proxy Patterns

"Design Patterns" Basic Principles of Design Patterns
"Design Patterns" Singleton Patterns "
Design Patterns" Factory Patterns
"Design Patterns" Prototype Patterns "
Design Patterns" Builder Patterns "Design Patterns
" Adapter Patterns "Design
Patterns" Bridge Patterns
"Design Patterns" "Decorator Mode
" Design Mode" Composition Mode
"Design Mode" Appearance Mode "Design Mode"
Flyweight Mode "Design Mode" Proxy Mode "
Design Mode
" Template Method Mode
"Design Mode" Command Mode

1. Basic introduction

Definition :

  • The proxy mode introduces a proxy object between the client and the target object. The proxy object completes the processing of the client request and passes the request to the target object, so that additional functional operations can be provided to extend the function of the target object . without modifying the target object itself.

The role composition of the proxy mode :

  • Abstract subject (Subject) : defines the public interface of the proxy and the target object, so that the proxy object can proxy the target object by implementing this interface.
  • Real subject (RealSubject) : defines the target object represented by the proxy object, and is the real object that the proxy object is to represent.
  • Proxy object (Proxy) : holds a reference to a real theme, and implements the same interface method as the real theme inside, and the client accesses the real theme through the proxy object. And, it can also extend the functionality of the real theme.

The proxy pattern class diagram is as follows :

insert image description here

2. Static proxy

Case background :

Before and after holidays, the railway station must be one of the places with the largest flow of people. Since the current online ticket purchase channels are very mature, you can buy tickets with a few simple operations on your mobile phone, which is very convenient. Even before the way of online ticket purchase came out, I remember that when I was young, I often saw the words of train ticket sales outlets posted in some stores. In the era when the Internet was not yet developed, this was relatively convenient. The way to buy tickets is much more convenient than going to the train station to buy tickets on the spot. If we use the idea of ​​agency model to analyze this scene in life, then the train station can be regarded as the target object , and the sales agency is the proxy object . We buy tickets through the agency, and both the train station and the agency have ticket sales. function, "we" is the access object .

The design class diagram is as follows :

insert image description here
Implementation steps of static proxy :

  • First , define an interface (abstract subject), and define a class (target object) that implements the interface.
  • Then , create a proxy class to implement the interface (abstract subject).
  • Finally , inject the target object into the proxy class, and call the method of the target object in the overloaded method of the proxy class, and add some operations before and after the method call of the target object as an extended function.

BookTicketsServiceinterface:

public interface BookTicketsService {
    
    
    void book();
}

BookTicketsServiceImplkind:

/**
 * 目标对象
 */
public class BookTicketsServiceImpl implements BookTicketsService{
    
    
    @Override
    public void book() {
    
    
        System.out.println("订票");
    }
}

BookTicketsProxykind:

public class BookTicketsProxy implements BookTicketsService{
    
    

    private final BookTicketsService bookTicketsService;

    public BookTicketsProxy(BookTicketsService bookTicketsService) {
    
    
        this.bookTicketsService = bookTicketsService;
    }

    @Override
    public void book() {
    
    
        System.out.println("订票前do something...");
        bookTicketsService.book();
        System.out.println("订票后do something...");
    }
}

Clientkind:

/**
 * 客户端类
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        BookTicketsService target = new BookTicketsServiceImpl();
        BookTicketsProxy bookTicketsProxy = new BookTicketsProxy(target);
        bookTicketsProxy.book();
    }
}

The access Clientobject BookTicketsProxyis booked through the proxy object, and the proxy object can extend an additional function before and after the booking.

Summary of static proxy :

  • On the premise of not modifying the function of the target object, the expansion of the target function is realized.
  • The proxy object needs to implement the same interface as the target object. There will be many proxy classes. When the interface methods increase, both the target object and the proxy object need to be maintained.

3. JDK dynamic proxy

JDK dynamic proxy is a dynamic proxy technology that comes with Java. It can dynamically create proxy objects at runtime and can only proxy interfaces . When using JDK dynamic proxy, you need to define an interface, and use java.lang.reflect.Proxy class and java.lang.reflect.InvocationHandler interface to dynamically create proxy objects.

Implementation steps of JDK dynamic proxy :

  • First , define an interface (abstract subject), and define a class (target object) that implements the interface.
  • Then , implement InvocationHandlerthe interface and rewrite invoke()the method, call the local method in invoke()the method , and add some extended operations here.
  • Finally , the proxy object Proxy.newProxyInstance()is created .

BookTicketsService interface:

public interface BookTicketsService {
    
    
    void book();
}

BookTicketsServiceImplkind:

public class BookTicketsServiceImpl implements BookTicketsService {
    
    
    @Override
    public void book() {
    
    
        System.out.println("订票");
    }
}

Custom MyInvocationHandlerclass :

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("订票前do something...");
        // 执行目标对象
        Object result = method.invoke(target, args);
        System.out.println("订票后do something...");
        return result;
    }
}

JdkProxyFactoryClass: proxy factory, create proxy objects:

/**
 * JdkProxy工厂,创建代理对象
 */
public class JdkProxyFactory {
    
    
    public static Object getObjectProxy(Object target) {
    
    
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 获取目标对象的加载器,加载代理类
                target.getClass().getInterfaces(), // 目标对象实现的接口
                new MyInvocationHandler(target) // 对应于代理对象自定义的InvocationHandler
        );
    }
}

Clientkind:

/**
 * 客户端类
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        BookTicketsService bookTicketsService = (BookTicketsService) JdkProxyFactory.getObjectProxy(new BookTicketsServiceImpl());
        bookTicketsService.book();
    }
}

In order to better observe the creation process of the dynamic proxy class in memory, you need to use the Java diagnostic tool arthas to print out the structure of the proxy class during the running of the program. The steps are as follows:

  1. Start the Client class to keep the program running, because the memory will be released after the program runs, so you cannot observe the structure of the dynamic proxy class during the running of the program, and print out the class name of the proxy object, which is convenient for later arthas tools to view , the Client class code is as follows:
public class Client {
    
    
    public static void main(String[] args) {
    
    
        BookTicketsService bookTicketsService = (BookTicketsService) JdkProxyFactory.getObjectProxy(new BookTicketsServiceImpl());
        bookTicketsService.book();

        System.out.println(bookTicketsService .getClass());

        while (true) {
    
    
            
        }
    }
}
  1. Download the arthas-jar tool, click to download and jump to the address
  2. Open the command line window and enter the root directory where arthas-boot.jar is located
  3. input the command:java -jar arthas-boot.jar
  4. Find the startup class name Client, and enter its corresponding serial number, as shown below:
    insert image description here
  5. After the loading is complete, enter the command jad com.sun.proxy.$Proxy0and wait to print out the structure of the proxy class during the running of the program, as shown below:
    insert image description here
  6. The complete code is as follows:
package com.sun.proxy;

import com.hzz.proxy.dynamicproxy.jdk.BookTicketsService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
extends Proxy
implements BookTicketsService {
    
    
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler invocationHandler) {
    
    
        super(invocationHandler);
    }

    static {
    
    
        try {
    
    
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.hzz.proxy.dynamicproxy.jdk.BookTicketsService").getMethod("book", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
    
    
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
    
    
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }

    public final boolean equals(Object object) {
    
    
        try {
    
    
            return (Boolean)this.h.invoke(this, m1, new Object[]{
    
    object});
        }
        catch (Error | RuntimeException throwable) {
    
    
            throw throwable;
        }
        catch (Throwable throwable) {
    
    
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
    
    
        try {
    
    
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
    
    
            throw throwable;
        }
        catch (Throwable throwable) {
    
    
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
    
    
        try {
    
    
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
    
    
            throw throwable;
        }
        catch (Throwable throwable) {
    
    
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void book() {
    
    
        try {
    
    
            this.h.invoke(this, m3, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
    
    
            throw throwable;
        }
        catch (Throwable throwable) {
    
    
            throw new UndeclaredThrowableException(throwable);
        }
    }
}
  • It can be seen that the proxy class $Proxy0actually implements BookTicketsService the interface, but it is realized for us through Proxy.newProxyInstance()the method , and it does not need to be implemented explicitly like in the static proxy, so it is true that both the proxy class and the target class must implement the same interface.
  • In addition, it can be seen from the source Proxy.newProxyInstance()code that targetthe target object is passed in the class created in the method, which is passed $Proxy0to the parent class of Proxy.

According to the structure of the class, the execution process of the dynamic proxy is roughly as follows :

  1. First , call the method on the proxy object Clientin book.
  2. After that , according to polymorphism, the method $Proxy0in book().
  3. Then , the method $Proxy0in callsbook() the method of the sub-implementation class object of the interface.InvocationHandlerinvoke()
  4. Finally , invoke()the method BookTicketsServiceImplexecutes book()the method in the target class through reflection.

JDK dynamic proxy summary :

  • When using JDK dynamic proxy, the privateand staticmethods cannot be proxied. The proxy class and the target class need to implement the same interface, because privateand staticcannot modify the interface.
  • The JDK dynamic proxy can only proxy the interface, not the common class, because the parent class of $Proxy0the class is Proxythe class, and multiple inheritance is not supported in Java.

4. CGLIB dynamic proxy

From the above two sections, we can know that whether it is a static proxy or a JDK dynamic proxy, the target class needs to implement an interface, but sometimes the target object is just a single object without implementing any interface. If you still want to use In proxy mode, you can use CGLIB dynamic proxy.

CGLIB (Code Generation Library) dynamic proxy :

  • CGLIB dynamically generates a subclass, which inherits the proxy class, rewrites all non- finaldecorated , and uses method interception technology in the subclass to intercept all method calls of the parent class.
  • CGLIB proxy provides a good supplement to JDK dynamic proxy. As a powerful and high-performance code generation package, it can provide proxy for classes that do not implement interfaces. It is widely used in AOP frameworks to implement method interception.

The jar coordinates for CGLIB are as follows:

<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
</dependency>

Implementation steps of JDK dynamic proxy :

  • First , define a target object.
  • Then , implement MethodInterceptorthe interface and rewrite the method, which is similar to the method interceptin the JDK dynamic proxy , and the method is used to intercept the method of the enhanced proxy class.invoke()intercept()
  • Finally , the proxy object is created through the methods Enhancerof the class create().

BookTicketsService class: target object

public class BookTicketsService {
    
    
    public void book() {
    
    
        System.out.println("订票");
    }
}

Custom MyMethodInterceptorclass :

public class MyMethodInterceptor implements MethodInterceptor {
    
    
    /**
     * 重写 intercept 方法,在该方法中调用目标对象的方法
     * @param o 代理对象
     * @param method 目标对象的方法的 method 实例
     * @param objects 方法entry
     * @param methodProxy 代理对象中的方法 method 实例
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        System.out.println("订票前do something");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("订票后do something");
        return result;
    }
}

CglibProxyFactorykind:

/**
 * CglibProxy工厂,创建代理对象
 */
public class CglibProxyFactory {
    
    
    public static Object getObjectProxy(Class<?> clazz) {
    
    
        // 创建Enhancer对象,类似于JDK动态代理的Proxy类
        Enhancer enhancer = new Enhancer();
        // 设置父类的字节码对象
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        // 创建代理对象
        return enhancer.create();
    }
}

Clientkind:

/**
 * 客户端类
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        BookTicketsService bookTicketsService = (BookTicketsService) CglibProxyFactory.getObjectProxy(BookTicketsService.class);
        bookTicketsService.book();
    }
}

Note for CGLIB proxies :

  • The bottom layer of the CGLIB package is to use the bytecode processing framework ASM to convert the bytecode and generate new classes.
  • To dynamically build subclasses in memory, the proxied class cannot be final, because if the inherited parent class is a constant class, it cannot be inherited, and an error will be reported java.lang.IllegalArgumentException.
  • The method of the target object cannot be privatebecause the subclass cannot access the private method of the parent class; the method of the target object cannot be finalbecause the subclass cannot override the immutable method of the parent class; the method of the target object cannot be staticbecause the static method belongs to the class , is not part of the object.

5. CGLIB and JDK dynamic proxy performance comparison experiment

In order to understand the performance of the two dynamic proxy methods of CGLIB and JDK, the following experiment is designed: using the two dynamic proxy methods of CGLIB and JDK to generate proxy objects, the number of operations is from 1000w times to 100 million times, and the number of each comparison operation is increased by 1000w times . The Java environment of the experiment is JDK1.6, JDK1.8 and JDK11 respectively, and the cglib version is 2.2.2.

5.1 Experimental source code

Abstract theme interfaceBookTicketsService :

public interface BookTicketsService {
    
    
    void book();
}

Target audienceBookTicketsServiceImpl :

public class BookTicketsServiceImpl implements BookTicketsService {
    
    
    @Override
    public void book() {
    
    
        int arr[] = {
    
    8, 5, 3, 2, 4};

        // 冒泡排序
        for (int i = 0; i < arr.length; i++) {
    
    
            // 外层循环,遍历次数
            for (int j = 0; j < arr.length - i - 1; j++) {
    
    
                // 内层循环,升序(如果前一个值比后一个值大,则交换)
                // 内层循环一次,获取一个最大值
                if (arr[j] > arr[j + 1]) {
    
    
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }

        // 选择排序
        for (int i = 0; i < arr.length; i++) {
    
    
            // 默认第一个是最小的。
            int min = arr[i];
            // 记录最小的下标
            int index = i;
            // 通过与后面的数据进行比较得出,最小值和下标
            for (int j = i + 1; j < arr.length; j++) {
    
    
                if (min > arr[j]) {
    
    
                    min = arr[j];
                    index = j;
                }
            }
            // 然后将最小值与本次循环的,开始值交换
            int temp = arr[i];
            arr[i] = min;
            arr[index] = temp;
        }

        // 插入排序
        for (int i = 1; i < arr.length; i++) {
    
    
            // 外层循环,从第二个开始比较
            for (int j = i; j > 0; j--) {
    
    
                // 内存循环,与前面排好序的数据比较,如果后面的数据小于前面的则交换
                if (arr[j] < arr[j - 1]) {
    
    
                    int temp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = temp;
                } else {
    
    
                    // 如果不小于,说明插入完毕,退出内层循环
                    break;
                }
            }
        }
    }
}

Implement the custom MyMethodInterceptorclass :

public class MyMethodInterceptor implements MethodInterceptor {
    
    
    /**
     * @param o 代理对象
     * @param method 被拦截的方法
     * @param objects 方法entry
     * @param methodProxy 调用目标对象方法的代理
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        Object result = methodProxy.invokeSuper(o, objects);
        return result;
    }
}

CGLIB dynamic proxy factory CglibProxyFactory class:

/**
 * CglibProxy工厂,创建代理对象
 */
public class CglibProxyFactory {
    
    
    public static Object getObjectProxy(Class<?> clazz) {
    
    
        // 创建动态代理增强对象
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new MyMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

Implement the custom MyInvocationHandlerclass :

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 {
    
    
        // 执行目标对象
        Object result = method.invoke(target, args);
        return result;
    }
}

JDK dynamic proxy factory JdkProxyFactory class :

/**
 * JdkProxy工厂,创建代理对象
 */
public class JdkProxyFactory {
    
    
    public static Object getObjectProxy(Object target) {
    
    
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 获取目标类的加载器,加载目标类
                target.getClass().getInterfaces(), // 代理需要实现的接口
                new MyInvocationHandler(target) // 对应于代理对象自定义的InvocationHandler
        );
    }
}

Test comparison ComparsionTestclass :

public class ComparisonTest {
    
    

    public static void main(String[] args) {
    
    
        BookTicketsService bookTicketsService = new BookTicketsServiceImpl();
        BookTicketsService jdkProxy = (BookTicketsService) JdkProxyFactory.getObjectProxy(bookTicketsService);
        BookTicketsService cglibProxy = (BookTicketsService) CglibProxyFactory.getObjectProxy(BookTicketsServiceImpl.class);
        long[] runCounts = new long[]{
    
    10000000, 20000000, 30000000, 40000000, 50000000, 60000000, 70000000, 80000000, 90000000, 100000000};
        Map<String, BookTicketsService> proxyMap = new HashMap<>(3);
        proxyMap.put("jdkProxy", jdkProxy);
        proxyMap.put("cglibProxy", cglibProxy);
        runWithoutMonitor(proxyMap);
        runWithMonitor(proxyMap, runCounts);
    }

    // 预热
    private static void runWithoutMonitor(Map<String, BookTicketsService> proxyMap) {
    
    
        for (int i = 0; i < 1; i++) {
    
    
            for (String key : proxyMap.keySet()) {
    
    
                for (int j = 0; j < 100000; j++) {
    
    
                    proxyMap.get(key).book();
                }
            }
        }

    }

    private static void runWithMonitor(Map<String, BookTicketsService> proxyMap, long[] runCounts) {
    
    
        for (int i = 0; i < runCounts.length; i++) {
    
    
            System.out.println("------------------[jdk version = 1.8.0_301] [运行"+runCounts[i]+"次]---------------------");
            for (String key : proxyMap.keySet()) {
    
    
                long start = System.currentTimeMillis();
                for (int j = 0; j < runCounts[i]; j++) {
    
    
                    proxyMap.get(key).book();
                }
                long end = System.currentTimeMillis();
                System.out.println("["+ key + "] Elapsed Time:" + (end-start) + "ms");
            }
        }

    }
}

5.2 Experimental results

5.2.1 Experimental results under jdk 1.6 version

Screenshot of running results in the IDE :

insert image description here

Cglib and jdk dynamic proxy performance comparison experiment data :

Number of runs cglib time consumption (ms) jdk time consumption
10000000 359 365
20000000 610 624
30000000 907 929
40000000 1191 1226
50000000 1492 1589
60000000 1807 1853
70000000 2095 2159
80000000 2450 2515
90000000 2756 2841
100000000 3052 3155

Histogram of cglib and jdk dynamic proxy performance comparison :

insert image description here

5.2.2 Experimental results under jdk 1.8 version

Screenshot of running results in the IDE :

insert image description here

Cglib and jdk dynamic proxy performance comparison experiment data :

Number of runs cglib time consumption (ms) jdk time consumption
10000000 284 292
20000000 502 431
30000000 744 668
40000000 910 866
50000000 1158 1115
60000000 1379 1292
70000000 1543 1512
80000000 1811 1713
90000000 2028 1951
100000000 2292 2143

Histogram of cglib and jdk dynamic proxy performance comparison :

insert image description here

5.2.3 Experimental results under jdk 11 version

Screenshot of running results in the IDE :

insert image description here

Cglib and jdk dynamic proxy performance comparison experiment data :

Number of runs cglib time consumption (ms) jdk time consumption
10000000 360 325
20000000 494 439
30000000 642 631
40000000 836 836
50000000 1054 1060
60000000 1267 1254
70000000 1487 1454
80000000 1685 1650
90000000 1931 1893
100000000 2087 2045

Histogram of cglib and jdk dynamic proxy performance comparison :

insert image description here

5.3 Analysis of experimental results

  • It can be seen that in the JDK 1.6 environment, the performance of the JDK dynamic proxy is indeed not as good as that of the CGLIB dynamic proxy, and the time consumption required by the JDK dynamic proxy object is obviously more than that of the CLGIB dynamic proxy object.
  • At that time, in JDK1.8 version, the time consumption required by JDK dynamic proxy objects has been comparable to or even surpassed the time performance of CGLIB dynamic proxy. At least it is certain that in the 1.8 environment, the efficiency of JDK dynamic proxy has been improved a lot. , which is mainly due to the optimization and upgrade of reflection technology in JDK.
  • 同样地,在JDK11环境下,JDK动态代理的性能依旧保持较好,不会再像JDK1.6时的动态代理性能那么差了。JDK8 和 JDK11 是目前市面上使用较多的两个版本,甚至以后会升级到 JDK17 版本,因为从 springboot 3.0 开始只支持 JDK 17以上版本。
  • 本次实验我使用的 CGLIB 版本为 2.2.2,其实在较新的 3.3.0 版本实验中,CGLIB 动态代理表现出来的性能依然比 JDK 动态代理好的,当时不会好 10 倍以上。
  • 目前,JDK动态代理和CGLIB动态代理的性能并不会相差很多,使用哪种动态代理方式往往不是因为性能的差别,而是因为这两种代理方式本身的特点决定的,JDK动态代理只支持接口,而CGLIB动态代理既支持接口又支持类,往往出于场景的考虑决定使用哪种代理方式,性能差距我觉得可以忽略不记

6. 区别比较

静态代理和动态代理的区别

  • 如果接口增加一个方法,静态代理除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。
  • 动态代理最大的优点就是接口中声明的所有方法都被转移到调用处理器的方法 InvocationHandler.invoke 中处理。 在接口方法数量比较多的时候,可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

JDK 代理和 CGLIB 代理的区别

  • 如果有接口则使用 JDK 动态代理,如果没有接口则使用 CGLIB 动态代理,因为 CGLIB 不仅可以代理接口还能代理普通类。
  • JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。CGLIB 使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性。在某些情况下,类的方法执行会比较高效

7. 代理模式在框架中的应用

我们知道,Spring 中有一个重要的概念就是面向切面编程(AOP),它很好地与 OOP 相配合,可以帮助我们做一下日志处理、权限控制以及事务管理的操作。因此,Spring 中对 AOP 的实现机制也有相关说明:

insert image description here

大概意思就是:

  • Spring 官方默认使用标准的 JDK 动态代理作为 AOP 的代理方式,可以代理任何接口。
  • 如果一个业务对象没有实现任何接口,那么 AOP 就会使用 CGLIB 动态代理。

但是,在 SpringBoot 中我们发现,对于 SpringBoot 2.x 以前的版本,默认使用的是 JDK 动态代理实现 AOP,具体可以见类 AopAutoConfiguration

SpringBoot 1.5 版本中 AOP 的代理机制:

@Configuration
@ConditionalOnClass({
    
     EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
    
    

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
			matchIfMissing = true)
	public static class JdkDynamicAutoProxyConfiguration {
    
    

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = false)
	public static class CglibAutoProxyConfiguration {
    
    

	}
}

在 SpringBoot 1.5 版本中:

  • SpringBoot 默认使用 JDK 动态代理作为 AOP 代理的方式。

SpringBoot 2.7 版本中 AOP 的代理机制:

@Configuration
@ConditionalOnClass({
    
     EnableAspectJAutoProxy.class, Aspect.class, Advice.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
    
    

	@Configuration(
            proxyBeanMethods = false
        )
        @EnableAspectJAutoProxy(
            proxyTargetClass = false
        )
        @ConditionalOnProperty(
            prefix = "spring.aop",
            name = {
    
    "proxy-target-class"},
            havingValue = "false",
            matchIfMissing = false
        )
        static class JdkDynamicAutoProxyConfiguration {
    
    
            JdkDynamicAutoProxyConfiguration() {
    
    
            }
        }

	@Configuration(
            proxyBeanMethods = false
        )
        @EnableAspectJAutoProxy(
            proxyTargetClass = true
        )
        @ConditionalOnProperty(
            prefix = "spring.aop",
            name = {
    
    "proxy-target-class"},
            havingValue = "true",
            matchIfMissing = true
        )
        static class CglibAutoProxyConfiguration {
    
    
            CglibAutoProxyConfiguration() {
    
    
            }
        }
}

在 SpringBoot 2.7 版本中:

  • SpringBoot 默认使用 CGLIB 动态代理作为 AOP 代理的方式。

至于为什么要更改 springboot 中 AOP 默认代理方式,首先看一下之前 Spring 的开发者们在 github 上的讨论

insert image description here

As shown in the figure above: Phil Webb mentioned the use @EnableTransactionManagement(proxyTargetClass = true), that is to say, it is recommended to use the CGLIB dynamic proxy method, in case someone does not use the interface, it can avoid some annoying problems . The annoying problem here is caused by the limitation that JDK dynamic proxy cannot proxy classes, but can only proxy interfaces . Therefore, starting from springboot 2.x, the default method of AOP proxy is CGLIB dynamic proxy instead of JDK dynamic proxy.

Of course, if you insist on using JDK dynamic proxy, you can also specify it in the configuration file:spring.aop.proxy-target-class = false

Guess you like

Origin blog.csdn.net/weixin_43252521/article/details/128506984