java pattern design-dynamic proxy

Keywords: Java Spring jvm

In learning Spring, we know that Spring has two main ideas, one is IoC, the other is AOP. For IoC, dependency injection needn't be said much. For Spring's core AOP, we not only need to know how to satisfy our functions through AOP, but also need to learn the underlying principle, and the principle of AOP is the motive of java. The state proxy mechanism, so this essay is a review of the dynamic mechanism of java.

In the dynamic proxy mechanism of java, there are two important classes or interfaces, one is Invocation Handler (Interface) and the other is Proxy(Class), which are necessary to implement our dynamic proxy. First, let's look at how the API Help Document of Java describes these two classes:

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

Translated:

Invocation Handler is an interface implemented by a call handler of a proxy instance.

Each proxy instance has a related call handler. When a method is invoked on a proxy instance, the method call is coded and sent to the invocation method of the invocation handler.

Each dynamic proxy class must implement the InvocationHandler interface, and each proxy class instance is associated with a handler. When we call a method through a proxy object, the invoke method of the InvocationHandler interface will be forwarded for invoke. Let's look at the invoke method, the only method of InvocationHandler interface:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

We see that this method accepts three parameters, so what do these three parameters represent?

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy:  Referring to the real object we represent
method:  It refers to a method that we call on a real object.Methodobject
args:  Refers to the parameters accepted when calling a method of a real object

If it's not clear, I'll explain these parameters more deeply through an example later.

Next, let's look at the class Proxy:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

The function of Proxy class is to create a class of proxy object dynamically. It provides many methods, but the newProxyInstance method is the most used one.

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

The function of this method is to get a dynamic proxy object, which receives three parameters. Let's see what these three parameters represent.

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

loader:  One ClassLoader Object, which defines ClassLoader Object to load the generated proxy object

interfaces:  OneInterfaceAn array of objects represents what interfaces I will provide to the object I need to proxy. If I provide a set of interfaces to it, the proxy object claims to have implemented the interface.(polymorphic),So I can call the methods in this set of interfaces.

h:  One InvocationHandler Object, which represents which dynamic proxy object I associate with when calling a method InvocationHandler On object

Well, after introducing these two interfaces (classes), let's take an example to see what our dynamic proxy model looks like:

First, we define an interface of type IUserIntetrface and declare two methods for it:


package proxy;

public interface IUserIntetrface {

    public String getName();
    String sayLanguage();

}


Then, we define a class to implement this interface. This class is our real object, User class:

package proxy;

public class User implements IUserIntetrface {

    @Override
    public String getName() {

        return "my name is xiao tang dou";
    }

    @Override
    public String sayLanguage() {
        return "i say English and Chinese";
    }

}

Next, we will define a dynamic proxy class. As mentioned earlier, every dynamic proxy class must implement the InvocationHandler interface, so our dynamic proxy class is no exception:

package proxy;

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

public class UserProxy implements InvocationHandler{

    //This is the real object we want to represent.
    private Object user;

    //    Constructing method, assigning initial value to the real object we want to proxy
    public UserProxy(Object user)
    {
        this.user = user;
    }

    /**
     * Object object Referring to the real object we represent
     * Method method It refers to the Method object of a method that we call a real object.
     * Object[] args Refers to the parameters accepted when calling a method of a real object
     */
    @Override
    public Object invoke(Object object, Method method, Object[] args)throws Throwable{
        //We can add our own operations before proxying real objects
        System.out.println("i come from china");
        System.out.println("Method:" + method);
        //    When a proxy object invokes a method of a real object, it automatically jumps to the invoke method of the handler object associated with the proxy object for invocation.
        method.invoke(user, args);
        //We can also add our own operations after proxying real objects.
        System.out.println("and you?");
        return null;
    }

}

Finally, let's look at our ClientTest test test class:

package proxy;

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


public class ClientTest {
        public static void main(String[] args)
        {
            //    The Real Object We Want to Act for
            IUserIntetrface user = new User();

            //    If we want to proxy which real object, we pass it in and finally call its method through the real object.
            InvocationHandler handler = new UserProxy(user);

            /*
             * Using Proxy's new Proxy Instance method to create our proxy object, let's look at its three parameters
             * The first parameter, user.getClass().getClassLoader(), is used here to load our proxy object using the ClassLoader object of the handler class.
             * The second parameter is user.getClass().getInterfaces(). The interface we provide here for the proxy object is the interface implemented by the real object, which means that I want to proxy the real object, so that I can call the methods in this group of interfaces.
             * The third parameter, handler, is the proxy object that we associate with the InvocationHandler above.
             */
            IUserIntetrface userinterface = (IUserIntetrface)Proxy.newProxyInstance(
                    user.getClass().getClassLoader(), user
                    .getClass().getInterfaces(), handler);
         System.out.println("-----------"+userinterface.getClass().getName());
            userinterface.getName();
            userinterface.sayLanguage();

Execute the output of the code console above:

-----------com.sun.proxy.$Proxy0
i come from china
Method:public abstract java.lang.String proxy.IUserIntetrface.getName()
and you?
i come from china
Method:public abstract java.lang.String proxy.IUserIntetrface.sayLanguage()
and you?

Let's first look at the $Proxy0 thing, and we see that this thing is printed out by System.out.println(userinterface.getClass().getName()); so why do we return the class name of this proxy object?

IUserIntetrface userinterface = (IUserIntetrface)Proxy.newProxyInstance(
                    user.getClass().getClassLoader(), user
                    .getClass().getInterfaces(), handler);

Maybe I thought the returned proxy object would be an IUser Intetrface type object or an InvocationHandler object, but the result is not. First, let's explain why we can convert it into an IUser Intetrface type object here. The reason is that on the second parameter of the new Proxy Instance method, we provide a set of interfaces for this proxy object, and then my proxy object will implement these interfaces. At this time, of course, we can transform this proxy object coercive type into any one of these interfaces, because the interface here is IUserIntetrface type, so we can. To convert it to the IUserIntetrface type.

At the same time, we must remember that the proxy object created by Proxy. new Proxy Instance is an object dynamically generated at the jvm runtime. It is not our InvocationHandler type, nor the set of interfaces we define, but an object dynamically generated at the runtime, and its naming is in the form of $starting, proxy being the middle, and finally proxy being the last. A number represents the label of an object.
Let's take a look next.

userinterface.getName();
 userinterface.sayLanguage();

These two methods are implemented by calling the method in the interface through the proxy object, then the program will jump to the invoke method in the handler associated with the proxy object to execute, and our handler object accepts a parameter of User type, indicating that I want to proxy the real object, so the handler will be called at this time. The invoke method is used to execute:

/**
     * Object object The real object we represent in this case is a User.
     * Method method It refers to a Method object that we call a method of a real object, which in this case is a method in userinterface.
     * Object[] args Refers to the list of parameters accepted when calling a method of a real object
     */
    @Override
    public Object invoke(Object object, Method method, Object[] args)throws Throwable{
        //We can add our own operations before proxying real objects
        System.out.println("i come from china");
        System.out.println("Method:" + method);
        //    When a proxy object invokes a method of a real object, it automatically jumps to the invoke method of the handler object associated with the proxy object for invocation.
        method.invoke(user, args);
        //We can also add our own operations after proxying real objects.
        System.out.println("and you?");
        return null;
    }

We can see that when we call a method of a real object through a proxy object, we can add our own operations before and after the method. At the same time, we can see that our method object is like this:

Method:public abstract java.lang.String proxy.IUserIntetrface.getName()
Method:public abstract java.lang.String proxy.IUserIntetrface.sayLanguage()

It's just two methods in our Userinterface interface, which proves that when I call a method through a proxy object, I actually delegate it to the invoke method of the handler object that it is related to. It's not called by myself, but by proxy.

This is our java Dynamic Proxy Mechanism

Posted by mahdi_20 on Tue, 26 Mar 2019 01:51:30 -0700