Dynamic proxy, dynamic proxy, dynamic proxy, first it is dynamic, then it can implement the proxy pattern.

The words “dynamic” and “agent” are used throughout the article.

What is dynamic proxy

Java’s dynamic proxy is first used to implement the “proxy pattern”.

It is then dynamic and has the flexibility to proxy “any class” (which of course requires an interface).

Let’s first look at what dynamic proxies can do.

As shown above, classes TestA and TestB want to call TargetObject0 and TargetObject1… The invoke method of the InvocationHandler is required for any method of the target object.

Notice that this is talking about any method of the TargetObjectX object

Notice that this is talking about any method of the TargetObjectX object

Notice that this is talking about any method of the TargetObjectX object

Why is it dynamic?

First, the InvocationHandler class can delegate almost any method of almost any class.

Java then implements the above proxy functionality by dynamically creating proxy classes.

Yes, classes are created dynamically. Java creates classes dynamically while the process is running, not at compile time.

Yes, classes are created dynamically. Java creates classes dynamically while the process is running, not at compile time.

Yes, classes are created dynamically. Java creates classes dynamically while the process is running, not at compile time.

Later in this article, we’ll talk more about how Java implements any kind of proxy functionality by dynamically creating proxy classes.

How to implement a simple dynamic proxy:

Go straight to the code. The code is not hard. So let’s look at the main method from the top down.

public class DynamicProxyMain {

  public static void main(String[] args) {
    //1) Create the target object
    TargetObject targetObject = new TargetObject();

    //2) Create a proxy object
    ITarget proxyObject = (ITarget) Proxy.newProxyInstance(
      TargetObject.class.getClassLoader(),
      TargetObject.class.getInterfaces(),
      new DynamicProxyHandler(targetObject)// Notice the DynamicProxyHandler here
    );

    //3) Call targetObject getVoid() via proxyObject
    proxyObject.getVoid();/ / ITarget interface method calls, direct agents to DynamicProxyHandler. The invoke () method}}Copy the code
class DynamicProxyHandler implements InvocationHandler {
  private Object target;

  public DynamicProxyHandler(Object target) {
    this.target = target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("---- call started, object :" + target + "; Methods:" + method.getName());
    // Call the target object's methods via reflection, or not. It's up to you
    Object ret = method.invoke(target, args);
    System.out.println("---- end of call, object :" + target + "; Methods:" + method.getName() + ", return value" + ret+ "\n");
    returnret; }}Copy the code

Definition of the target class

interface ITarget {
    void getVoid(a);
}

class TargetObject implements ITarget {// Be sure to implement the interface ITarget
    public void getVoid(a) {
        System.out.print("TargetObject.getVoid(); \n"); }}Copy the code

Demo log Output:

- the call, object: com. Java. Study. Base. Proxy. TargetObject @ 6 f94fa3e; Methods: getVoid TargetObject. GetVoid (); - the call ended, object: com. Java. Study. Base. Proxy. TargetObject @ 6 f94fa3e; Method :getVoid, return nullCopy the code

Note that it needs to be emphasized here:

The propped target class TargetObject must have an interface: the ITarget interface.

The principle of dynamic proxy

So what object does the proxy.newProxyInstance () method create?

Next, we can use the main method a little bit:

public class DynamicProxyMain {

  public static void main(String[] args) {
    Object proxyObject = Proxy.newProxyInstance(TargetObject.class.getClassLoader(), TargetObject.class.getInterfaces(), new DynamicProxyHandler(new TargetObject()));

    // what is the class of the dynamic proxyObject?
    // What will the following log output be?
    System.out.println("\ n \ nproxyObject class =" + proxyObject.getClass().getSimpleName());
    System.out.println("ProxyObject implementation interface ="+ Arrays.toString(proxyObject.getClass().getInterfaces())); }}Copy the code

What is the output of the log in the main function above?

What is the output of the log in the main function above?

What is the output of the log in the main function above?

proxyObject.getClass().getSimpleName()

Output: proxyObject class =$Proxy0

proxyObject.getClass().getInterfaces();

The interfaces being implemented by the log output: proxyObject = [interface com. Java. Study. Base. Proxy. ITarget]

As you can see from the log, proxy.newProxyInstance () returns an object of class $Proxy0 that implements the ITarget interface.

The “Proxy0″ class is the Java dynamically created class we mentioned earlier. The Proxy0″ class is the Java dynamically created class we mentioned earlier.” The Proxy0″ class is the Java dynamically created class we mentioned earlier.” The Proxy0″ class is the real proxy class, and all methods invoked through this object are proxied to the Invoke method of the DynamicProxyHandler.

The Invoke method of DynamicProxyHandler can decide whether to call the corresponding method of TargetObject, and can do special processing before and after the call.

Let’s look at the proxy.newproxyInstance () method again:

public static Object newProxyInstance($Proxy0, $Proxy0, $Proxy0) >[] interfaces,// dynamically create the proxy class $Proxy0 to implement all interfaces// All method calls to the created proxy object will be propped to the invocationController.invoke () method
Copy the code

Let’s continue combing through the class diagram of the demo above

So the uncle has a question to ask:

Can the above demo proxy class, $Proxy0, only proxy methods defined by the ITarget interface?

Can the proxy class, $Proxy0, only proxy methods defined by the ITarget interface?

Can the proxy class, $Proxy0, only proxy methods defined by the ITarget interface?

Can the proxy class, $Proxy0, only proxy methods defined by the ITarget interface?

If the call Object. The equals () if you can agent to DynamicProxyHandler. The invoke () method?

The following demo:

public static void main(String[] args) {
    Object proxyObject = Proxy.newProxyInstance(TargetObject.class.getClassLoader(), TargetObject.class.getInterfaces(), new DynamicProxyHandler(newTargetObject())); ) ; proxyObject.equals(proxyObject);/ / the equals method will agent to DynamicProxyHandler. The invoke () method?

}
Copy the code

The answer is yes. ProxyObject. The equals (), proxyObject. HashCode (), proxyObject. ToString () will be acting to DynamicProxyHandler. The invoke () method.

Java is a static language, how to generate $xy0 dynamically?

Let’s follow the source code of java8.

Note: Java8 source code; Different versions of Java source code may vary: github.com/JetBrains/j…

Note: Java8 source code; Different versions of Java source code may vary: github.com/JetBrains/j…

Note: Java8 source code; Different versions of Java source code may vary: github.com/JetBrains/j…

public class Proxy implements java.io.Serializable {


  private static final class ProxyClassFactory implements BiFunction<ClassLoader.Class<? > [].Class<? >>{

    @Override
    publicClass<? > apply(ClassLoader loader, Class<? >[] interfaces) {/ /...
      //1) Generate $Proxy0 bytecode, store it in byte array [proxyClassFile]
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
      try {
        //2) Parse the bytecode of Class $Proxy0 into a Class object and load the Class $Proxy0 into the ClassLoader.
        return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
      } catch (ClassFormatError e) {
        /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). * /
        throw newIllegalArgumentException(e.toString()); }}}}Copy the code
Let’s take a look:How is the bytecode generated for $Proxy0?

So we find the source code for the ProxyGenerator class. Github.com/JetBrains/j…

public class ProxyGenerator {
  public static byte[] generateProxyClass(finalString var0, Class<? >[] var1,int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    final byte[] var4 = var3.generateClassFile();
    / /...
    return var4;
  }

  
  /** * generateClassFile(); * Focus on generateClassFile() function; * Focus on generateClassFile() function; * We can see: * Byte by byte is written as a stream [DataOutputStream], bytecode. * and then export, DataOutputStream. ToByteArray () export byte []. * * /
  private byte[] generateClassFile() {
    / /...
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);
    // Start writing the bytecode to the dout stream ------ If you want to explore the Java bytecode file format you can Google: Java bytecode files
    dout.writeInt(0xCAFEBABE);//0xCAFEBABE, look here, look here, first write 4 bytes: bytecode magic
    dout.writeShort(CLASSFILE_MINOR_VERSION);// Continue writing 2 bytes: minor version
    dout.writeShort(CLASSFILE_MAJOR_VERSION);// Write another 2 bytes: major version Main version
    cp.write(dout);
    dout.writeShort(accessFlags);
    dout.writeShort(cp.getClass(dotToSlash(className)));
    dout.writeShort(cp.getClass(superclassName));
    dout.writeShort(interfaces.length);
    / /...
    //
    returnbout.toByteArray(); }}Copy the code
So let’s move on,Now that we have bytecode in the form of byte[], how do we parse it to class?

We continue to find the source code for Proxy defineClass0()

public class Proxy implements java.io.Serializable {

	// The return defineClass0() function is native
  private static nativeClass<? > defineClass0(ClassLoader loader, String name,byte[] b, int off, int len);
}
Copy the code

The proxy.defineclass0 () method is written in C.

The proxy.defineclass0 () method is written in C.

The proxy.defineclass0 () method is written in C.

Unhurriedly, continue to find his C source.

Let’s pull out the C language we learned in college.

And then look: Proxy. C

#include <stdlib.h>
#include <string.h>

#include "jvm.h"
#include "io_util.h"


JNIEXPORT jclass JNICALL
  Java_java_lang_reflect_Proxy_defineClass0(JNIEnv *env, jclass ignore, jobject loader, jstring name, jbyteArray data, jint offset, jint length)
{
  / /...
  jbyte *body;
  char *utfName;
  body = (jbyte *)malloc(length);
  
	/ /...
  //1) Copy the bytecode data array to the body array;
  // In a Java virtual machine, the memory of arrays is allocated on the heap and released by the Java GC, so c needs to do some processing before accessing Java arrays.
  (*env)->GetByteArrayRegion(env, data, offset, length, body);

	/ /...
  
  //2) copy the Java string name to the c string pointer utfName.
  if(name ! =NULL) {
    jsize len = (*env)->GetStringUTFLength(env, name);
    jsize unicode_len = (*env)->GetStringLength(env, name);
    if (len >= (jsize)sizeof(buf)) {
      utfName = malloc(len + 1);
      if (utfName == NULL) {
        JNU_ThrowOutOfMemoryError(env, NULL);
        gotofree_body; }}else {
      utfName = buf;
    }
    (*env)->GetStringUTFRegion(env, name, 0, unicode_len, utfName);
    VerifyFixClassname(utfName);
  } else {
    utfName = NULL;
  }

  //3) The key is here
  // The key method is here
  // The key method is here
  // DefineClass() to build: proxy class. And return.
  // This method is a standard API of JNI, so we will not dig into the source code of jNI env.
  result = (*env)->DefineClass(env, utfName, loader, body, length);

  / /...
  
  return result;
}
Copy the code

Five, the summary

Finally, let’s sum it up in one sentence:

Java is the core of the dynamic Proxy, through a Proxy. NewProxyInstance (this, interfaces, invocationHandler) to dynamically create the Proxy class, all the function call agent into the invocationHandler.

A limitation of dynamic proxy is that the proxy object must have an interface.

Dynamic proxies are used in very few scenarios and are rarely used in normal development, especially android client development.

Dynamic proxy: Java dynamic proxy: Java dynamic proxy: Java dynamic proxy






If you can, uncle wants to dynamically proxy up your like function.

Well, if you can’t, just give it a thumbs up before you go, Buddy. 💖