9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

table of Contents:

  • 1. Agency Model
  • 2. Static proxy
  • 3. Dynamic Agent
    • 3.1. JDK dynamic proxy mechanism
    • 3.2. CGLIB dynamic proxy mechanism
    • 3.3. Comparison of JDK dynamic proxy and CGLIB dynamic proxy
  • 4. Comparison of static proxy and dynamic proxy
  • 5. Summary

1. Agency Model

The agent model is a better understood design pattern. Simply put,  we use proxy objects to replace access to real objects, so that we can provide additional functional operations and extend the functionality of the target object without modifying the original target object.

The main function of the proxy mode is to expand the functions of the target object. For example, you can add some custom operations before and after a certain method of the target object is executed.

For example: yours asked Xiaohong to help you interrogate, Xiaohong regarded as my agent, and the agent's behavior (method) was interrogation.

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

The proxy mode has two implementation methods: static proxy and dynamic proxy. Let's first look at the implementation of static proxy mode.

2. Static proxy

In the static proxy, the enhancement of each method of the target object is done manually (the code will be demonstrated in detail later ), which is very inflexible (for example, once a new method is added to the interface, the target object and the proxy object must be modified ) and troublesome ( Need to write a separate proxy class for each target class ).  The actual application scenarios are very, very few, and the use of static agents is almost invisible in daily development.

Above we are static agents from the perspective of implementation and application. From the perspective of JVM,  static agents turn interfaces, implementation classes, and proxy classes into actual class files at compile time.

Static proxy implementation steps:

  1. Define an interface and its implementation class;
  2. Create a proxy class to also implement this interface
  3. Inject the target object into the proxy class, and then call the corresponding method in the target class in the corresponding method of the proxy class. In this way, we can block access to the target object through the proxy class, and can do what we want before and after the target method is executed.

The following code shows!

1. Define the interface for sending SMS

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

2. Implement the interface for sending SMS

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

3. Create a proxy class and also implement the interface for sending SMS

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

4. Actual use

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

After running the above code, the console prints:

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

As you can see from the output, we have added the send() method of SmsServiceImpl.

3. Dynamic Agent

Compared with static agents, dynamic agents are more flexible. We do not need to create a separate proxy class for each target class, and we do not need to implement an interface, we can directly proxy the implementation class (  CGLIB dynamic proxy mechanism ).

From the perspective of the JVM, the dynamic agent dynamically generates the class bytecode at runtime and loads it into the JVM.

Speaking of dynamic proxy, Spring AOP and RPC framework should be two must mention, their implementation all rely on dynamic proxy.

Dynamic agent is relatively small in our daily development, but it is almost a necessary technology in the framework. After learning dynamic agents, it is also very helpful for us to understand and learn the principles of various frameworks.

As far as Java is concerned, there are many ways to implement dynamic proxy, such as  JDK dynamic proxy , CGLIB dynamic proxy and so on.

guide-rpc-framework[1]  uses JDK dynamic proxy, let’s take a look at the use of JDK dynamic proxy first.

In addition, although  guide-rpc-framework[2]  does not use the  CGLIB dynamic proxy, we will briefly introduce its use and comparison with the JDK dynamic proxy.

3.1. JDK dynamic proxy mechanism

3.1.1. Introduction

In the Java dynamic proxy mechanism, the  InvocationHandler  interface and the  Proxy  class are the core.

The most frequently used method in the Proxy class is: newProxyInstance(), this method is mainly used to generate a proxy object.

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

This method has 3 parameters:

  1. loader  : Class loader, used to load proxy objects.
  2. interfaces  : some interfaces implemented by the proxy class;
  3. h  : an object that implements the InvocationHandler interface;

To implement dynamic proxy, you must also implement InvocationHandler to customize the processing logic. When our dynamic proxy object invokes a method, the invocation of this method will be forwarded to the invoke method that implements the InvocationHandler interface class for invocation.

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

The invoke() method has the following three parameters:

  1. proxy  : dynamically generated proxy class
  2. method  : corresponds to the method called by the proxy class object
  3. args  : the parameters of the current method method

In other words: when you  call the method of the proxy object created by  the newProxyInstance() of the Proxy class  , it will actually call  the invoke() method of the class that implements the InvocationHandler interface  .  You can customize the processing logic in the invoke() method, such as what to do before and after the method is executed.

3.1.2. JDK dynamic proxy class use steps

  1. Define an interface and its implementation class;
  2. Customize InvocationHandler and override the invoke method. In the invoke method, we will call the native method (method of the proxy class) and customize some processing logic;
  3. Create a proxy object through the Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) method;

3.1.3. Code example

This may be a bit hollow and difficult to understand. Let’s take a look at my last example!

1. Define the interface for sending SMS

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

2. Implement the interface for sending SMS

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

3. Define a JDK dynamic proxy class

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * @author shuang.kou
 * @createTime 2020年05月11日 11:23:00
 */
public class DebugInvocationHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private final Object target;
    public DebugInvocationHandler(Object target) {
        this.target = target;
    }    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return result;
    }
}

invoke() method: When our dynamic proxy object calls a native method, it actually calls the invoke() method, and then the invoke() method replaces us to call the native method of the proxy object.

4. Get the factory class of the proxy object

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类的类加载
                target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
        );
    }
}

getProxy(): Mainly obtain the proxy object of a certain class through the Proxy.newProxyInstance() method

5. Actual use

SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");

After running the above code, the console prints:

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

3.2. CGLIB dynamic proxy mechanism

3.2.1. Introduction

One of the most fatal problems of JDK dynamic proxy is that it can only proxy classes that implement interfaces.

In order to solve this problem, we can use CGLIB dynamic proxy mechanism to avoid it.

CGLIB[3] ( Code Generation Library ) is a bytecode generation library based on ASM[4] , which allows us to modify and dynamically generate bytecode at runtime. CGLIB implements proxy through inheritance. Many well-known open source frameworks have used CGLIB[5] , such as the AOP module in Spring: if the target object implements the interface, the JDK dynamic proxy is used by default, otherwise the CGLIB dynamic proxy is used.

The MethodInterceptor  interface and the  Enhancer  class are the core in the CGLIB dynamic proxy mechanism  .

You need to customize MethodInterceptor and override the intercept method. Intercept is used to intercept methods that enhance the proxy class.

public interface MethodInterceptor
extends Callback{
    // 拦截被代理类中的方法
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}
  1. obj  : the object being proxied (the object that needs to be enhanced)
  2. method  : the intercepted method (the method that needs to be enhanced)
  3. args  : method parameter
  4. methodProxy  : used to call the original method

You can dynamically obtain the proxy class through the Enhancer class. When the proxy class calls a method, the intercept method in the MethodInterceptor is actually called.

3.2.2. Steps to use CGLIB dynamic proxy class

  1. Define a class;
  2. Customize MethodInterceptor and override the intercept method. The intercept method is used to intercept and enhance the proxy class method, which is similar to the invoke method in the JDK dynamic proxy;
  3. Create a proxy class through create() of the Enhancer class;

3.2.3. Code example

Unlike the JDK dynamic proxy, no additional dependencies are required. CGLIB[6] ( Code Generation Library ) actually belongs to an open source project. If you want to use it, you need to manually add related dependencies.

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

1. Implement a class that uses Alibaba Cloud to send SMS

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

2. Custom  MethodInterceptor (method interceptor)

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * 自定义MethodInterceptor
 */
public class DebugMethodInterceptor implements MethodInterceptor {
    /**
     * @param o           被代理的对象(需要增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object object = methodProxy.invokeSuper(o, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return object;
    }
}

3. Get the proxy class

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

4. Actual use

AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");

After running the above code, the console prints:

9 minutes to take you to understand the proxy mode, static proxy, JDK+CGLIB dynamic proxy

 

3.3. Comparison of JDK dynamic proxy and CGLIB dynamic proxy

  1. JDK dynamic proxy can only proxy classes that implement interfaces, while CGLIB can proxy classes that do not implement any interfaces.  In addition, CGLIB dynamic proxy intercepts method calls of the proxy class by generating a subclass of the proxy class, so it cannot proxy classes and methods declared as final.
  2. In terms of the efficiency of the two, in most cases, JDK dynamic proxy is better. With the upgrade of JDK version, this advantage becomes more obvious.

4. Comparison of static proxy and dynamic proxy

  1. Flexibility  : The dynamic proxy is more flexible, without having to implement an interface, it can directly proxy the implementation class, and there is no need to create a proxy class for each target class. In addition, in a static proxy, once a new method is added to the interface, the target object and the proxy object must be modified, which is very troublesome!
  2. JVM level  : The static proxy turns the interface, implementation class, and proxy class into actual class files at compile time. The dynamic agent dynamically generates class bytecode at runtime and loads it into the JVM.

5. Summary

This article mainly introduces two implementations of proxy mode: static proxy and dynamic proxy. Covers the actual combat between static and dynamic agents, the difference between static and dynamic agents, and the difference between JDK dynamic agents and Cglib dynamic agents.

Recommended reading for high-quality articles:

Alibaba advanced interview questions (first issue, 136 high-frequency questions, including answers)

https://blog.csdn.net/weixin_45132238/article/details/107251285

GitHub Biaoxing 20w's 4 low-level interview guides (computer bottom layer + operating system + algorithm), interview headlines/Tencent is right!

https://blog.csdn.net/weixin_45132238/article/details/108640805

Alibaba internal architecture combat: SpringBoot/SpringCloud/Docker/Nginx/distributed

https://blog.csdn.net/weixin_45132238/article/details/108666255

Guess you like

Origin blog.csdn.net/weixin_45132238/article/details/108736629