Java dynamic proxy is actually very simple

When using Java dynamic proxy, I have always been confused. What is dynamic proxy and where is dynamic? What is the difference between it and static proxy? But unfortunately, I didn't find a blog that can really tell me the reason, so I decided to do it myself and analyze it.

Firstly, the main focus of this paper is as follows:

Of course, I won't pay much attention to the specific implementation. The purpose of this blog is to tell you what is a dynamic agent and what its process is.

First, what is a dynamic agent?

I copy a big man's explanation as follows:

Using Java reflection technology, create a new class (also known as "dynamic proxy class") and its instance (object) to implement some given interfaces at runtime. The proxy is interfaces, not classes or abstract classes. The specific implementation is only known at runtime. spring aop is based on this principle.

Is there a problem with this explanation? No problem, it's very simple and clear, but for new students, especially those who have just started, there is only one feeling, that is MMP?

It is our goal to reject the official discourse and make technology simple for everyone.

All right, let's start our tutorial: Let's start with a simple demo.

First, this is an interface:

public interface Subject {
    void play();
}

It's very simple. Its function is just to print a sentence.

Then there is an extension class that implements it:

public class XiaoQing implements Subject {
    @Override
    public void play() {
        System.out.println("I'm Xiaoqiang. I'm going running");
    }
}

Finally, let's look at our specific processing class

public class TestProxy implements InvocationHandler {
    private Subject subject;

    public TestProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("I'm a dynamic agent");
        method.invoke(subject, args);
        System.out.println("end");
        return null;
    }

  
  
    public static void main(String[] args) {
        XiaoQing xiaoQing = new XiaoQing();
        TestProxy testProxy = new TestProxy(xiaoQing);
        //Get the proxy object
        Subject subject = (Subject) Proxy.newProxyInstance(xiaoQing.getClass().getClassLoader(), xiaoQing.getClass().getInterfaces(), testProxy);
        subject.play();
    }
}

Let's put aside the main method first. Let's focus on the TestProxy class, which implements an InvocationHandler interface and the invoke method.

Let's take a look at the invoke method:

Students familiar with reflection should find that we call method Invoke () method and pass in our interface object and parameter args To call the methods in our interface. In other words, we can insert the corresponding pre and post execution methods from here.

Continue our process:

Let's take a look at the main method, which is our test entry:

It is also very simple. We mainly focus on the Proxy class.

public class Proxy implements java.io.Serializable {
		. . . 
    protected InvocationHandler h;

   . . . 

It can be found that it holds an invocationhandler interface object internally. What does this object do? Let's click to see:

Very simple. There is only one method, invoke, which is commonly known as the interception method.

interface InvocationHandler{
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
 InvocationHandler Is created by the proxy instance<i>Call handler</ i>Implemented interface. * * <p>Each proxy instance has an associated invocation handler. When a method is called by a proxy instance, the call of that method will be encoded and scheduled to its calling handler{@code invoke} *method

Simple and crude, that is to say, each proxy class has an associated call processing class, and the function of this interface is to schedule the proxy method we call to the real call. That is, call its invoke method.

It may sound a little windy. It doesn't matter. Let's hold this question for the time being and take a look at proxy Newproxyinstance method:

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

Through the annotation, we understand that its function is to return the instance of our proxy class. Notice the three parameters we passed in. The first is classLoader and class loader. The second is the interface class implemented by our real object. The third is the processing class implemented by ourselves.

With the above analysis, you may still have doubts. Let's take a look at the proxy class generated by dynamic proxy to find out:

Due to the reasons, we can find a generated proxy class on the Internet and see that the difference will not be great:

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

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke((Object)this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException v0) {
            throw v0;
        }
        catch (Throwable var2_2) {
            throw new UndeclaredThrowableException(var2_2);
        }
    }

    public final String sayHello(String string) {
        try {
            return (String)this.h.invoke((Object)this, m3, new Object[]{string});
        }
        catch (Error | RuntimeException v0) {
            throw v0;
        }
        catch (Throwable var2_2) {
            throw new UndeclaredThrowableException(var2_2);
        }
    }

   . . . 
    }
}

Let's take a look at the sayHello method. Let's take sayHello as the play method in the interface just now, and then observe the internal implementation:

Where h is the invocationHandler. When we call the interface play method, the proxy class calls the invoke method through the invocationHandler object, passes in the proxy object, method and the parameters passed during our call, and then locates the method we really want to call, So we can insert our corresponding code before and after calling the method to realize the dynamic proxy.

Finally, let's summarize some problems in our mind map at the beginning:

InvocationHandler ? Why implement it?

InvocationHandler is equivalent to a delegate processing. We implement its invoke method, so that we can dynamically insert our own code into the invoke to realize the corresponding logic.

Proxy.newProxyInstance?

Proxy. The main function of newproxyinstance is to generate our proxy object through reflection, so as to associate our proxy object with the incoming InvocationHandler object.

Invoke?

It is convenient for us to insert our own code dynamically. When the generated proxy object calls the interface method, we actually call the invoke method and pass in the proxy object, the real method and the corresponding parameters.

Therefore, the so-called dynamic proxy actually dynamically generates a proxy object during initialization through the class loader. This proxy object implements all the methods of our interface, and calls the invoke method internally through the invocationHandler object during subsequent calls, so as to locate our real call location, This allows us to insert our corresponding code before and after the call.

The above is just my personal understanding. If you have any questions, you are welcome to raise them.

Added by josh_mcqueen on Wed, 09 Feb 2022 14:24:12 +0200