Dynamic agent and static agent

1, Static proxy

Static agent is actually the agent pattern in design pattern.

In the design mode, as shown in the above figure, the proxy mode is to call the func method rewritten by class B that implements the func method of interface A, which is called by the third proxy class C that implements the func method of interface A.

//The interface of the method that requires a proxy     
interface A{ function func() }


//Actual implementation
class B implement A{
    @Override
    function  func(){}
}


//proxy class
class C implement A{
    A a;//Built in an object that actually executes the func method
    C(A a){this.a=a; }
    @Override
    function func(){
        a.func();
    }
}


Call:
A proxy =new C(new B())
proxy.func()//The actual output is method a of B

That is, the method calling b is called through the c proxy.

For example, Xiaogang is sleeping in the bedroom, but the teacher needs to call the roll in class. Xiaogang asks Xiaoming to answer for him in class. At this time, the action is the interface method. Xiaoming is Xiaogang's agent class, which replaces Xiaogang. This is the implementation of static proxy.

Create a new Person interface, which has an action to go to class.

public interface Person {
    void gotoClass();
}

Another Student interface is built to implement the gotoClass method of Person. Normal students who go to class will answer the teacher's roll call. At this time, rewrite the gotoClass method.

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    @Override
    public void gotoClass() {
        System.out.println(name + ": Yes, I'm here!");
    }
}

However, Xiaogang didn't go to the classroom, but asked Xiaoming to go to class instead of him. Xiaoming is an agent object.

/**
* Student agent class also realizes the action of class
* A {@ link Person} object needs to be built in. This object is the {@ link Student} of the real class. The agent only does the replacement work
*/
public class StudentProxy implements Person {
    public StudentProxy(Person person) {
        this.person = person;
    }
    private Person person;
    @Override
    public void gotoClass() {
        beforeMethod();
        person.gotoClass();
    }
    //Perform the agent's pre class preparations, such as mental activities
    public void beforeMethod() {
        System.out.println("I thought to myself: I'm a student in place of the class. Shouldn't the teacher find out?");
    }
}

Here, Xiao Ming emphasizes the gotoClass method, but in fact, it's not Xiao Ming's answer, but Xiao Gang's answer. Therefore, in gotoClass, what should be implemented is the gotoClass() of the actual execution object.

Perform the following:

public static void main(String[] args) {
    Student xiaoGang=new Student("Xiao Gang");
    StudentProxy xiaoming =new StudentProxy(xiaoGang);
    xiaoming.gotoClass();
}

Output results:
I thought to myself: I'm a student in place of the class. Shouldn't the teacher find out?
Xiao Gang: Yes, I am!

It can be seen that Xiaoming executes gotoClass, but actually outputs Xiaoming's psychological activities and Xiaogang's answer, and Xiaogang's answer is executed by Xiaoming's agent.

This pattern is the proxy pattern, that is, the static proxy.

2, Dynamic agent

If a project needs many agents, it needs to create different agent classes, which becomes very troublesome.

For example, there is another program ape class whose main job is to write code. At this time, a Developer class and IDeveloper interface are required. IDeveloper has a writeCode method, and Developer implements the IDeveloper interface.

//Write code interface
public interface IDeveloper {
    void writeCode();
}
//Programmer class
public class Developer implements IDeveloper {
    private String name;
    public Developer(String name) {
        this.name = name;
    }
    @Override
    public void writeCode() {
        System.out.println("Developer " + name + " writes codes.");
    }
}

If the programmer Xiao Wang asks for leave, he needs Xiao Zhang's agent to write code. In this case, it is necessary to create a proxy class to write code.

public class DeveloperProxy implements IDeveloper {
    private IDeveloper developer;
    public DeveloperProxy(IDeveloper developer) {
        this.developer = developer;
    }
    @Override
    public void writeCode() {
        before();
        this.developer.writeCode();
    }
    public void before(){
        System.out.println("He asked for leave. I'm the agent who writes code.");
    }
}

Perform the following:

public static void main(String[] args) {
    Developer  xiaozhang = newDeveloper("Xiao Zhang");
    IDeveloper xiaowang = new DeveloperProxy(xiaozhang);
    xiaowang.writeCode();
}

Output results:
He asked for leave. I'm the agent who writes the code.
Developer Xiao Zhang writes codes.

This will be very troublesome in a project. At this time, dynamic agents are needed.

The class of dynamic proxy needs to implement the InvocationHandler interface, and then override the invoke method. Calling method. in the invoke method Invoke (target, args) to implement the proxy. Here, the actual function of target is the same as that of a object in class C above, but a is an object of type A and target is an object of type T, which ensures that the dynamic agent can represent different types of interfaces.

/**
* The dynamic proxy class needs to implement the {@ link InvocationHandler} interface
* */
public class MyDynamicProxy<T> implements InvocationHandler {
    public MyDynamicProxy(T target) {
        this.target = target;
    }

    private T target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target, args);
    }
}

At this time, you can use this proxy class to proxy whether the above Student replaces class or Developer instead of writing code.

public static void main(String[] args) {
    InvocationHandler xiaomingHandler=new MyDynamicProxy<Person>(new Student("Xiao Gang"));
    Person xiaohong = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, xiaomingHandler);
    xiaohong.gotoClass();
    System.out.println();
    InvocationHandler developHandler=new MyDynamicProxy<IDeveloper>(new Developer("Xiao Zhang"));
    IDeveloper developer = (IDeveloper) Proxy.newProxyInstance(IDeveloper.class.getClassLoader(), new Class<?>[]{IDeveloper.class}, developHandler);
    developer.writeCode();
}
Output:
Xiao Gang: Yes, I am!

Developer Xiao Ming writes codes.

3, Principle of dynamic agent

Above, we created a dynamic Proxy object by using the newProxyInstance method of Proxy class. Check the source code of this method:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
    * Look up or generate the designated proxy class.
    */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
    * Invoke its constructor with the designated invocation handler.
    */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
       }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

It encapsulates the steps of creating a dynamic proxy class with the following two pieces of code.

final Class<?>[] intfs = interfaces.clone();
...
final Constructor<?> cons = cl.getConstructor(constructorParams);

In fact, we should pay more attention to the following line of code:

Class<?> cl = getProxyClass0(loader, intfs);

This line of code generates a proxy class, and the constructor in the following code is also obtained through the class generated here. It can be seen that the generation of this class is the key to the whole dynamic proxy. Because it is a dynamically generated class file, which is cached in the java virtual machine.

We can print it into the file by the following method:

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
String path = "xxxxxx/StudyProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
    fos.write(classFile);
    fos.flush();
    System.out.println("proxy class class File written successfully");
} catch (Exception e) {
    System.out.println("Write file error");
}

Decompile this class file. Let's see what jdk generates for us:

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
  private static Method m2;
    private static Method m3;
   private static Method m0;
    /**
    *Note that this is the construction method of generating proxy class, and the method parameter is InvocationHandler type
     *The proxy object calls methods to execute the invoke method in the InvocationHandler, and the InvocationHandler holds an instance of the proxy object.
    *
    *super(paramInvocationHandler),Is the constructor that calls the parent class Proxy.
    *Parent class holding: protected InvocationHandler h;
    *Proxy Construction method:
    * protected Proxy(InvocationHandler h) {
    * Objects.requireNonNull(h);
    * this.h = h;
    * }
    *
    */
    public $Proxy0(InvocationHandler paramInvocationHandler) throws {
        super(paramInvocationHandler);
    }

    /**
    *
    *Here, the giveMoney method of the proxy object is called, which directly calls the invoke method in the InvocationHandler and passes m3 in.
    *this.h.invoke(this, m3, null);It's simple and clear.
    *The proxy object holds an InvocationHandler object, and the InvocationHandler object holds an object to be proxied,
    */
    public final void giveMoney() throws {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error|RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable){
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    ......

    static {
        try {
            //Look at what's in the static block here. Did you find the giveMoney method. Remember the name given by giveMoney through reflection m3, and ignore the others first
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}

You can see that jdk generates a proxy class called $Proxy0 for our (0 after this name is the number, and multiple proxy classes will increase at one time). This class file is placed in memory. When we create a proxy object, we obtain the construction method of this class through reflection, and then create a proxy instance. Through the generated proxy class source code, you can clearly see the specific process of dynamic proxy implementation.

In fact, InvocationHandler is an intermediary class that holds the proxy object, and the corresponding method of the proxy object is invoked in the invoke method. Hold the reference of the proxy object through aggregation, and finally turn the external call to invoke into the call to the proxy object.

When the proxy class calls its own method, it calls the invoke method of the mediation class object through its own mediation class object, so as to achieve the proxy to execute the method of the proxy object. In other words, the dynamic agent realizes the specific agent function through the intermediary class.

Keywords: Java

Added by php.ajax.coder on Wed, 19 Jan 2022 13:19:34 +0200