Java notes - Dynamic Proxy

Keywords: Java Back-end Dynamic Proxy Proxy

Let's look at a piece of code to simulate the addition, deletion, modification and query function of the database
Define an interface, which is the function list of the database

package test.dynamicagent;

public interface User {
    public abstract void add();  //increase
    public abstract void delete(); //Delete
    public abstract void update(); //change
    public abstract void select(); //check
}

Implementation class

package test.dynamicagent;

public class UserImpl1 implements User {
    @Override
    public void add() {
        System.out.println("Additional functions of simulation database");
    }

    @Override
    public void delete() {
        System.out.println("Simulate the deletion function of the database");
    }

    @Override
    public void update() {
        System.out.println("Simulate database update function");
    }

    @Override
    public void select() {
        System.out.println("Query function of simulation database");
    }
}

Here, replace the database operation statement with four output statements
Write a test class and run it

package test.dynamicagent;

public class UserDemo1 {
    public static void main(String[] args) {
        UserImpl1 user1 = new UserImpl1();
        user1.add();
        user1.delete();
        user1.update();
        user1.select();
    }
}


The results were printed out normally
But now we have to consider a problem. This is the specific class UserImpl1 for database operation, which implements the four functions of addition, deletion, modification and query. However, the actual situation is that permission verification is required before adding statements, and there is also a logging function after adding statements.

So how can we add permission verification and logging functions without changing the original code.
We can't modify the original code. We can consider writing a proxy object. The proxy object will realize more code functions based on the original object. That is, proxy object = original object + new code

Here we will introduce the concept of static proxy and create a proxy class for the above UserImpl1 concrete class, which will also implement the User interface. Then, the object of UserImpl1 is passed into the proxy class. The proxy class calls the methods of adding, deleting, modifying and querying through this object, and then adds the statements to realize the functions of permission verification and logging.

Static proxy class

package test.dynamicagent;

public class UserImpl2 implements User {
    public User user1;
    public UserImpl2(UserImpl1 userImpl1){
        this.user1 = userImpl1;
    }
    @Override
    public void add() {
        System.out.println("Simulate database permission verification");
        user1.add();
        System.out.println("Simulate database logging");
        System.out.println("--------------------------");
    }

    @Override
    public void delete() {
        System.out.println("Simulate database permission verification");
        user1.delete();
        System.out.println("Simulate database logging");
        System.out.println("--------------------------");
    }

    @Override
    public void update() {
        System.out.println("Simulate database permission verification");
        user1.update();
        System.out.println("Simulate database logging");
        System.out.println("--------------------------");
    }

    @Override
    public void select() {
        System.out.println("Simulate database permission verification");
        user1.select();
        System.out.println("Simulate database logging");
        System.out.println("--------------------------");
    }
}

package test.dynamicagent;

public class UserDemo1 {
    public static void main(String[] args) {
        UserImpl1 user1 = new UserImpl1();

        UserImpl2 user2 = new UserImpl2(user1);
        user2.add();
        user2.delete();
        user2.update();
        user2.select();
    }
}


It can be seen that we did not modify the code in the original UserImpl1 class, but just wrote a proxy class. This proxy class not only implements the functions of adding, deleting, modifying and querying UserImpl1 class, but also adds the functions of permission verification and logging.

Introduction of dynamic agent

However, static proxy has an obvious disadvantage. A static proxy class can only serve one class. If we have multiple classes that need proxy, we need to write multiple proxy classes. This is too much trouble, so we will think, can we not write the proxy class, but get the object of the proxy class?

In fact, objects are created based on Class objects. Each Class will only have one Class object. This Class object will get all the information of this Class, including methods, variables, etc. Therefore, we do not need to write a proxy Class. As long as we can get the Class object of a proxy Class, we can create a proxy Class object based on this Class object. So, where should we get this Class object. At this time, you can see the above example. In the example, both the proxy Class and the concrete Class implement the same interface. As mentioned earlier, the proxy Class is equivalent to adding new code and functions to the concrete Class. Therefore, if we want to create a corresponding proxy Class for a concrete Class, we can start from their common interface.

However, there is a new problem here. Class objects contain all the information of the class. Naturally, they contain constructors. Only with constructors can objects be created. However, the interface does not have a constructor, that is, even if the class object of the interface is obtained, the object cannot be created.

In this case, Java provides a java.lang.reflect.Proxy class and a java.lang.reflect.InvocationHandler interface for the proxy class. This class and an interface are the core content of dynamic proxy.

There is a method in the Proxy class, public static class <? > Getproxyclass (classloader, class <? >... interfaces).

The function of this getProxyClass method is to pass in a Class loader and a set of interfaces, and then return a Class object of a proxy Class. The Class object obtained in this way has all the Class information of the interface and constructor. Therefore, this Class object can be used to create proxy Class objects.

This constructor is Proxy(InvocationHandler h) {}. According to this constructor, a Proxy proxy class object will be created. But we found that the constructor needs to pass in a parameter InvocationHandler h, what is this?

InvocationHandler interface is an interface implemented by the calling handler of proxy proxy instance. Each proxy instance has an associated calling handler; When a method is called by a proxy instance, the method call is encoded and dispatched to the invoke method of the calling handler.

It's hard to understand just the concept here. Now let's take a look at the example of dynamic agent

Create a proxy Class object by creating a Class object

First create a student interface. What students need to complete is the exam

package test.dynamicagent.agent;

public interface Student {
    public abstract void examination();
}

Then there is the specific implementation class StudentImpl, that is, the proxy class

package test.dynamicagent.agent;

public class StudentImpl implements Student {

    @Override
    public void examination() {
        System.out.println("Student examination");
    }
}

Then there is the concrete class that implements the InvocationHandler interface

package test.dynamicagent.agent;

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

public class StudentHandler implements InvocationHandler {
    private Object obj;

    public StudentHandler(){}

    //Pass in the object of the proxy class and put it into obj
    public StudentHandler(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Add new functions to be completed by proxy class
        System.out.println("Check the stationery and get the test paper");
        Object invoke = method.invoke(obj, args);
        //Add new functions to be completed by proxy class
        System.out.println("Check the answers and hand in the test paper");
        return invoke;
    }
}

Finally, the test class, the main method to run the agent class

package test.dynamicagent.agent;

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

public class StudentDemo {
    public static void main(String[] args) throws Exception {
        //Proxied object
        StudentImpl s1 = new StudentImpl();
        //Get proxy Class object
        Class proxyStudent = Proxy.getProxyClass(s1.getClass().getClassLoader(),s1.getClass().getInterfaces());
        //Get construction method according to Class object
        Constructor cons = proxyStudent.getConstructor(InvocationHandler.class);
        //Create an object for the InvocationHandler interface
        StudentHandler handler = new StudentHandler(s1);
        //Create a proxy class object according to the obtained construction method, and pass in the parameter handler
        Student proxy = (Student)cons.newInstance(handler);
        //Executing methods using proxy class objects
        proxy.examination();
    }
}

The result is

The result is correct. In the original agent class, this is to print a student exam. Here, through the agent class, the function of checking stationery and receiving test papers before the student exam, checking answers and handing in test papers after the exam is realized.

When we call a method through a proxy class object, proxy. Examination();
As mentioned earlier, the InvocationHandler interface is an interface for the proxy class object to implement the call handler. Implementing this interface is actually a call handler for the proxy class object, that is, the handler in the example. This call handler will go to the invoke method of the specific class that implements the InvocationHandler interface when the proxy class object calls the examination method.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Add new functions to be completed by proxy class
        System.out.println("Check the stationery and get the test paper");
        Object invoke = method.invoke(obj, args);
        //Add new functions to be completed by proxy class
        System.out.println("Check the answers and hand in the test paper");
        return invoke;
    }

The invoke Method actually uses the reflection mechanism, that is, directly calling the member methods in the class through the Method object. The specific Method object depends on which Method the proxy class object calls at this time.

Here, the proxy class object calls the examination() Method, so the Method object corresponds to the examination() Method, and the member Method examination in the obj object is called through invoke. Who is the obj object here? It is the object of the specific implementation class StudentImpl class (that is, the proxied class) passed into the interface.

Therefore, in fact, what is called is the examination method of the proxied class object s1, not the proxy method of the proxy class object

So you can see that three statements are actually executed

System.out.println("Check the stationery and get the test paper");  //Statement of proxy class
Object invoke = method.invoke(obj, args); //Call the method of the proxied class
 System.out.println("Check the answers and hand in the test paper"); //Statement of proxy class

In fact, it is very similar to a static proxy. It also uses the statements added by the proxy class to be clamped one after another, and the statements are executed by the methods of the proxy class
But dynamic proxy doesn't need to write another proxy class

Create proxy class objects directly through newProxyInstance

In fact, the above method of creating a Class object and then creating a proxy Class object with a Class object is still a little cumbersome. Java provides a newProxyInstance method, which can hide the process of creating a Class object and directly obtain a proxy Class object.
Using the above example, the code of the implementation class of the student interface, the proxy class and the InvocationHandler interface does not need to be modified, but only the main method needs to be modified

package test.dynamicagent.agent;

import java.lang.reflect.Proxy;

public class StudentDemo1 {
    public static void main(String[] args) {
        //Create the object of the proxied class
        StudentImpl student = new StudentImpl();
        //Create a concrete implementation class for the StudentHandler interface
        StudentHandler handler = new StudentHandler(student);

        /*
            Use the Proxy.newProxyInstance method to create a proxy class object. Let's talk about the meaning of the parameters in brackets
            student.getClass().getClassLoader()Is to get the class loader to load the proxy object
            student.getClass().getInterfaces()Let the proxy object and the proxy object have the same interface,
                                 In this way, the proxy object can call the methods in the interface like the proxy object
            handler Is the object of the class created above that implements the InvocationHandler interface
         */
        Student proxy = (Student) Proxy.newProxyInstance(student.getClass().getClassLoader()
                ,student.getClass().getInterfaces()
                ,handler);
        proxy.examination();
    }
}

The result is the same

Posted by greedyisg00d on Wed, 03 Nov 2021 10:22:49 -0700