Thousands of birds fly away, thousands of people disappeared.

A boat suoliweng, fishing alone cold river snow

— Tang Liu Zongyuan, “River Snow”

It was first posted on my official account

Understand dynamic proxies in depth

An overview,

While reading retrofit’s source code recently, one of the key areas is dynamic proxies. Looking back on dynamic proxies, I found that some of the details were not fully understood before. This post will take a closer look at dynamic proxies.

Two, about the agency

Chinese nationality is an implicit nationality, emphasizing subtle and indirect communication. Indirect communication between objects is also an important aesthetic in object-oriented design. Demeter’s law also points out that “one object should keep the least understanding of other objects”, and indirect communication can achieve the effect of “high cohesion, low coupling”. Agent is one of a kind of important means, such as life wechat business agent, manufacturer commissioned the acting sales of goods, we only deal with wechat business, don’t know who is “manufacturer”, behind the wechat business and manufacturers can be abstracted as “proxy class” and “delegate class”, thus, the realization of the hidden delegate class, realize decoupling between the client and delegate class, You can do some extra processing without modifying the delegate class

2.1 Introduction to agent Mode

In Java and Patterns, it is stated that “the proxy pattern provides an object with a proxy object that controls access to the original object.” The class diagram is shown below



Proxy objects in proxy mode can be found through the class diagramProxyAnd target objectsSubjectTo implement the same interface, the client callsProxyObject,ProxyYou can controlSubjectAccess, the real functionality implementation is inSubjectThe finished.

Applicable scenarios:

  • You don’t want certain classes to be accessed directly.
  • You want to do some pre-processing before access.
  • You want to control the memory and permissions of the objects to be accessed.

Advantages as follows

  • The proxy pattern accesses real objects by using a reference proxy object, where the proxy object acts as an intermediary between the client and the real object.
  • The proxy mode is mainly used for remote proxy, virtual proxy, and protected proxy. The protection agent can control access rights.

2.2. Static proxy

“Static” agent: if the agent class already exists before the program runs, it is usually called static agent. For example, wechat business A only acts as agent of facial mask of BRAND A, and consumers can buy facial mask of A factory through wechat business (control), in which both wechat business and factory realize the interface of Sell

Entrusted mask factory

class  FactoryOne :SellMask{
    override fun sell(a) {
        println("FactoryOne: Facial Mask from Factory A")}}Copy the code

Wechat business static agent

class BusinessAgent : SellMask {
    private lateinit var sellMask: SellMask

    init {
        sellMask = FactoryOne()
    }

    override fun sell(a) {
        println("BusinessAgent: wechat BusinessAgent starts advertising in moments of friends")
        sellMask.sell()
        print("BusinessAgent: Made a fortune.")}}Copy the code

A common interface

interface SellMask {
    fun sell(a)
}
Copy the code

2.3 comparison of similar patterns

Getting a reference allows you to do things like extensions. This looks similar to the decorator and adapter patterns, which are structural patterns, but the core of the proxy pattern is to provide a proxy for other objects to control access to that object.

Proxy mode VS adapter mode

Looks like, they can be seen as an object to provide a front interface, but the purpose of the adapter pattern is changed by considering the object interface, the proxy mode does not change the object of the agent’s interface, this point has obvious difference between two model, below are object adapter adapter UML diagrams and class





Proxy vs. decorator mode

The decorator mode has the same interface with the decorated object, which is the same as the proxy mode, but the decorator mode emphasizes the enhancement of the decorated object, while the proxy mode exerts control over the use of the object and does not provide the enhancement of the object itself. The proxied object is created by the proxy object, and the client does not even need to know that the proxied class exists; The decorated object is created by the client and passed to the decorated object. The decorator UML diagram is shown below

You think this is the end of it? Below 👇 is the highlight!

Third, in-depth understanding of dynamic proxy

3.1. What is dynamic proxy

The way proxy classes create proxies while the program is running is called dynamic proxies. That is, proxy classes are not defined in code, but are generated automatically at run time based on the information we “specify” in Java code. Static proxies tend to bloat code. The advantage of dynamic proxy over static proxy is that it is easy to uniformly handle the functions of the proxy class without modifying the functions of each proxy class. Or the above mask selling wechat business as an example, she in the purchase market after a comparison to decide which brand of mask agent.

3.2. How to use dynamic proxy

As in the previous article, wechat business and facial mask factory have implemented the Sell interface, which will not be described here. Let’s look at the difference. To achieve dynamic proxy, we need to implement the InvocationHandler interface

Implement the InvocationHandler interface

public class DynamicProxy implements InvocationHandler {
    private Object object;// The referenced agent

    public Object newProxyInstance(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Agent packaging sent to wechat moments");
        Object result = method.invoke(object,args);
        System.out.println("Agents make money.");
        returnresult; }}Copy the code
  • The target attribute represents a delegate object
  • InvocationHandlerIs the interface that the intermediate class responsible for connecting the proxy class to the delegate class must implement. Only one invoke function needs to be implemented
  • Invoke the function
public Object invoke(Object proxy, Method method, Object[] args)
Copy the code

Let’s take a closer look at the parameters of this core function

  • Proxy generation through dynamicproxy. NewProxyInstance (business) automatically generated proxy class $Proxy0. The class will be introduced in more detail below)
  • Method represents the function to which the proxy object is called, such as the Sell method in the sellMask interface
  • Args represents the argument that the agent calls the function vigorously, where the sell method has no argument

Each function that calls a proxy object actually ends up on the Invoke function of InvocationHandler, so you can do some uniform processing here, and the prototype of AOP has emerged. You can also make some judgments based on method method names to implement special processing of certain functions.

Using dynamic Proxies

fun main(args: Array<String>) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true")  // Add this to get the proxy class

    var maskFactory = FactoryMaskOne()

    var dynamicproxy: DynamicProxy = DynamicProxy()

    var sellMask: SellMask = dynamicproxy.newProxyInstance(maskFactory) as SellMask

    sellMask.sell()

}
Copy the code

We will delegate class mask project reached dynamicproxy FactoryMaskOne. NewProxyInstance, by using the following function returns a proxy objects

public Object newProxyInstance(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
Copy the code

The actual Proxy class is dynamically generated at this point, and subsequent calls to this Proxy class invoke directly. Let’s take a closer look at proxy.newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
Copy the code

The meanings of the three parameters of method are as follows:

  • Loader: a ClassLoder that defines the proxy class;
  • Interfaces: List of interfaces implemented by the proxy class
  • H: The calling handler, which is the instance of the class we defined above that implements the InvocationHandler interface

To summarize, the delegate class gets an instance of the dynamically generated proxy class (in this case, $proxy0.class) through the **newProxyInstance ** method, and then gains control of the delegate class by calling the proxy method. All calls to the proxy class actually go to the Invoke method, where we call the corresponding method of the delegate class and can add some of our own logic, such as unified handling of login, validation, and so on.

Dynamically generated proxy class $Proxy0

The ProxyGenerator class is not available in Android Studio. The ProxyGenerator class is available in the Sun.misc package, which uses the IntelliJ IDE to create Java projects

// Generate $Proxy0 class file
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
Copy the code

The generated proxy class in the com.sun.proxy package is complete with the following code

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

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}public final String toString(a) throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}public final void sell(a) throws  {
        try {
          // You can see that the interface methods are handled by the Invoke method of H, which is defined in the parent Proxy as the InvocationHandler interface
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}public final int hashCode(a) throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("dev.proxy.SellMask").getMethod("sell");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code

You can see this in the generated proxy class

  • Dynamically generated proxy classes are based on$ProxyIs the class name prefix inherited fromProxyAnd it didThe Proxy newProxyInstance (...).The second argument is passed to the classes of all interfaces.
  • Interface methods are handled by the Invoke method of H. H is defined as the InvocationHandler interface in the parent Proxy class, which is proxy.newProxyInstance (…). The third parameter of

3.4 How are dynamic proxy classes generated

  • Concern 1 proxy.newProxyInstance (…) function

The dynamic Proxy class is calling proxy.newProxyInstance (…) Function generated, condensed core code is as follows

public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
        
        finalClass<? >[] intfs = interfaces.clone(); .../* * Look up or generate the designated proxy class. */Class<? > cl = getProxyClass0(loader, intfs);/* * Invoke its constructor with the designated invocation handler. */
        try{...finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
            if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run(a) {
                        cons.setAccessible(true);
                        return null; }}); }// Then create a new proxy class object with InvocationHandler as an argument to the proxy class constructor
            return cons.newInstance(new Object[]{h});
        } catch(IllegalAccessException | InstantiationException e) {... }Copy the code

You can see that it’s called first getProxyClass(loader, interfaces) to get the dynamic proxy class, and then InvocationHandler as an argument to the proxy class constructor to create a new proxy class object.

  • Concern 2 Class
    cl = getProxyClass0(loader, intfs);

$Proxy+num = $Proxy+num = $Proxy+num = $Proxy+num = $Proxy+num

						/*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
Copy the code

summary

In this article, we will focus on dynamic proxies in the JDK. AOP and RetroFIT use this technique as one of the core mechanisms. But Java dynamic proxies are based on interfaces. That’s where CGLIB comes in. CGLIB(Code Generation Library) is an ASM-based bytecode Generation Library that allows you to modify and generate bytecode dynamically at run time. CGLIB implements the proxy through inheritance, which I won’t go into here.

Refer to the link

  • Juejin. Im/post / 684490…
  • Juejin. Im/post / 684490…
  • www.cnblogs.com/xiaoluo5013…
  • www.zhihu.com/question/20…
  • Blog.csdn.net/qq_27095957…
  • A.codekk.com/detail/Andr…
  • www.jianshu.com/p/0391a8e93…

Welcome to pay attention to my public number, learn together, improve together ~