JDK dynamic agent won't hit me after reading it

WHAT

Dynamic agent is a kind of agent mode. Agent mode refers to that the agent helps the proxy object complete what should be completed by the proxy object. Agents usually have more powerful capabilities and are more professional than the objects they are represented. Take an example in life to illustrate that you can choose to find the landlord and sign a contract to complete the house lease. However, we usually do not do this in life, but seek more professional intermediaries to act as agents for us and find landlords with rental intention. In this example, finding a house should have been completed by yourself, but it has become completed by an intermediary. You don't need to worry about how to complete it. This model is called the agent model.

WHY

Why is there an agent model?

The great progress of mankind comes from the division of labor and cooperation. Special people do special tasks and perform their respective duties, which can speed up the promotion of business without requiring everyone and every organization to be all-round. Everyone just needs to focus on their own one-third of an acre and hand over extra work to more professional people. You just have to 'sit back and enjoy your success'.

WHERE

It applies to everything you don't want to do, such as finding Ding Dong to buy vegetables for you

HOW

The above are ideological statements. How can we implement them in our Java world?

​ ! [(C:\Users \ Huang Xiaosi \ appdata \ roaming \ typora \ typera user images \ image-20210822120809841. PNG)

In order to improve the reusability of the code, the intermediary will sometimes call the lazy method directly, and sometimes do not call the lazy method directly. Where is the logic of using the lazy method implemented?

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-W8beLQPp-1629608306470)(C:\Users \ Huang Xiaosi \ appdata \ roaming \ typora user images \ image-20210822123645684. PNG)]

InvocationHandler interface

All mediations must be the implementation class of InvocationHandler interface, and need to implement the Invoke method, which is the method that specifically determines the execution logic of the mediation.


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * InvocationHandler Is the class that determines the specific proxy logic
 * @author yhg 2021-08-22 12:13
 */
public class MyProxyHandler implements InvocationHandler {

    private Object lazyPerson;

    public MyProxyHandler(Object lazyPerson) {
        this.lazyPerson = lazyPerson;
    }

    public Object getLazyPerson(){
        return lazyPerson;
    }

    /**
     *
     * @return  Return mediation object
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),lazyPerson.getClass().getInterfaces(),this);
    }

    /**
     * All actions that should be initiated by lazy people will be intercepted. After intercepting, this method will be executed. Here you can determine the specific logic of how the intermediary operates
     * @param proxy   intermediary
     * @param method  Proxy method
     * @param args    Parameters of the proxied method
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String res = null;
        System.out.println("intermediary:I'm looking for a house. Just sit down, lazy man");
        //If the intermediary decides not to find a lazy person to see the last house, the lazy person can not appear in the whole process of agent looking for a house
        System.out.println("intermediary:I've found the last house left. Let's go and see the house together");
        ((FindHouse)lazyPerson).findHouse();
        System.out.println("The intermediary and the lazy finally determined the house A");
        return "house A";
    }
}

Java face object, how is the mediation object generated?

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

The mystery is in the above line of code. This is the method provided by JDK to generate a class file and load a virtual machine at runtime.

Class loader classLoader is required because class files need to be loaded

To intercept lazy people's methods, you need to know which methods to intercept. These methods are defined in the interface

After intercepting the method, I need the InvocationHandler to tell me how to execute the logic, so I need this

What does the Java dynamically generated proxy class file look like
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.yhg.FindHouse;
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 FindHouse {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 new UndeclaredThrowableException(var4);
        }
    }

    public final String findHouse() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.yhg.day3.FindHouse").getMethod("findHouse");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

You can see that all the Proxy classes produced are subclasses of Proxy and are implemented in turn according to the interface type you pass in

public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
}
// The following is the code of super
protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
}

You can see that the constructor saves the InvocationHandler we passed in to the h attribute of the parent class

Looking back, the methods of each proxy class are executed

return (XXXX)super.h.invoke(this, mXXX, (Object[])null);

Note that the proxy class will finally execute the invoke method of the InvocationHandler we passed in

So far, all logic has been sorted out

The following is the rest of the code. The InvocationHandler class has been posted above


/**
 * @author yhg 2021-08-22 12:12
 */
public interface FindHouse {
    String findHouse();
}


/**
 * @author yhg 2021-08-22 12:13
 */
public class LazyPerson implements FindHouse {
    @Override
    public String findHouse() {
        System.out.println("The lazy man is looking for a house");
        return "";
    }
}


/**
 * @author yhg 2021-08-22 12:23
 */
public class FindHouseDemo {
    public static void main(String[] args) {
//        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        // Either of the above two lines of code can save the generated proxy class file in the project com\sun\proxy for easy understanding
        FindHouse proxy = (FindHouse)new MyProxyHandler(new LazyPerson()).getProxy();
        String house = proxy.findHouse();
        System.out.println("The intermediary found it" + house);


    }
}

Code run results

intermediary:I'm looking for a house. Just sit down, lazy man
 intermediary:I've found the last house left. Take the lazy man to see the house
 The lazy man is looking for a house
 The intermediary and the lazy finally determined the house A
 The agent found the house A


MyProxyHandler(new LazyPerson()).getProxy();
String house = proxy.findHouse();
System.out.println("intermediary found" + house);

}

}

Code run results

```java
 intermediary:I'm looking for a house. Just sit down, lazy man
 intermediary:I've found the last house left. Take the lazy man to see the house
 The lazy man is looking for a house
 The intermediary and the lazy finally determined the house A
 The agent found the house A


Keywords: Java Design Pattern AOP reflection

Added by satanclaus on Thu, 23 Dec 2021 09:10:23 +0200