Wechat search: code nong StayUp

Home address: goZhuyinglong.github. IO

Source: github.com/gozhuyinglo…

JDK dynamic proxies are instances of proxy classes that are dynamically generated by the JVM while the program is running, based on reflection. That is, proxy classes are not user-defined but generated by the JVM.

Since this principle is implemented through the Java reflection mechanism, it is necessary to have some understanding of the reflection mechanism before learning. Portal: Java Reflection mechanism: Follow the code to learn reflection

Here is the content of this article:

1. JDK dynamic proxy core classes

JDK dynamic proxies have two core classes, both under Java’s reflection package (java.lang.Reflect) : the InvocationHandler interface and the Proxy class.

1.1 InvocationHandler interface

The InvocationHandler of a proxy instance needs to implement the InvocationHandler interface, and each proxy instance has an associated InvocationHandler. When a method is invoked on a proxy instance, the method call is encoded and dispatched to the invoke method of its calling handler.

That is, each proxy instance we create will have an associated InvocationHandler and will be referred to the Invoke method of the InvocationHandler when the proxy instance’s method is called.

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
Copy the code

The Invoke method handles a method call on a proxy instance and returns a result.

It has three parameters, which are:

  • Proxy: is the proxy instance that invokes this method.
  • method: corresponds to interface methods called on the proxy instanceMethodInstance.
  • argsA:ObjectArray, which is the parameter value passed in a method call on the proxy instance. If the interface method has no parameters, the value is null.

The return value is: the return value of calling the method on the proxy instance.

1.2 the Proxy class

The Proxy class provides static methods for creating dynamic Proxy classes and their instances, which are also superclasses of dynamic Proxy classes.

A proxy class has the following properties:

  • The Proxy class name starts with “$Proxy” followed by a numeric ordinal number.
  • The proxy class inheritsProxyClass.
  • The proxy class implements the interface specified when it is created (JDK dynamic proxies are interface oriented).
  • Each proxy class has a common constructor that takes a single argument, the interfaceInvocationHandlerTo set the invocation handler for the proxy instance.

Proxy provides two static methods for retrieving Proxy objects.

1.2.1 getProxyClass

Get the Class object of the proxy Class and create the proxy instance by calling the constructor.

public staticClass<? > getProxyClass(ClassLoader loader, Class<? >... interfaces)throws IllegalArgumentException
Copy the code

This method takes two parameters:

  • Loader: indicates the class loader.
  • intefaces: indicates the interfaceClassObject array.

The Class object that returns the dynamic proxy Class.

1.2.2 newProxyInstance

Use to create a proxy instance.

public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
        throws IllegalArgumentException
Copy the code

This method takes three parameters:

  • Loader: indicates the class loader.
  • interfaces: indicates the interfaceClassObject array.
  • H: Specifies the calling handler.

Returns an instance of the proxy class for the specified interface.

1.3 summary

The Proxy class is mainly used to obtain dynamic Proxy objects, and the InvocationHandler interface is mainly used to constrain and enhance method calls.

2. Get code examples of proxy instances

The two static methods for obtaining a proxy instance were introduced in the previous chapter, and the implementation is now demonstrated with code examples.

2.1 Create the target interface and its implementation class

JDK dynamic proxies are interface-based, and we create an interface and its implementation class.

Foo interface:

public interface Foo {

    String ping(String name);

}
Copy the code

RealFoo implements the Foo interface:

public class RealFoo implements Foo {

    @Override
    public String ping(String name) {
        System.out.println("ping");
        return "pong"; }}Copy the code

2.2 Creating an InvocationHandler

Create an implementation class MyInvocationHandler for the InvocationHandler interface. The constructor parameter of this class is the target object to be proxied.

The invoke method is invoked by calling method’s invoke method, as described above.

Here temporarily do not understand it does not matter, the following source code analysis chapter will be analyzed.

public class MyInvocationHandler implements InvocationHandler {

    // Target object
    private final Object target;

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


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy - " + proxy.getClass());
        System.out.println("method - " + method);
        System.out.println("args - " + Arrays.toString(args));
        returnmethod.invoke(target, args); }}Copy the code

2.3 Method 1: Obtain the proxy instance using getProxyClass

The specific implementation steps are as follows:

  1. Gets the Class object of the proxy Class based on the Class loader and interface array
  2. Create an instance (of the proxy Class) through the constructor of the Class object
  3. Strong-cast the proxy instance to the target interface Foo (because the proxy class implements the target interface, it can).
  4. Finally, a proxy is used to make the method call.
@Test
public void test1(a) throws Exception {
    Foo foo = new RealFoo();
    // Get the Class object of the proxy Class based on the Class loader and interface arrayClass<? > proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);// Create an instance (of the proxy Class) through the constructor of the Class object
    Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class)
        .newInstance(new MyInvocationHandler(foo));

    // Call the ping method and print the return value
    String value = fooProxy.ping("Yang guo");
    System.out.println(value);

}
Copy the code

Output result:

proxy - class com.sun.proxy.$Proxy4 method - public abstract java.lang.String IO. Making. Gozhuyinglong. Proxy. Foo ping (Java. Lang. String) args - / Yang guo ping pongCopy the code

It can be seen from the output results:

  • The name of the proxy class is$ProxyAt the beginning of.
  • Method instance is the method invoked by the proxy class.
  • Parameters are the parameters passed when the proxy class calls the method.

2.4 Method 2: Obtain an agent instance using newProxyInstance

This is the simplest and recommended way to get the proxy object directly.

Note: the implementation of this method is actually the same as the process of using getProxyClass method above.

@Test
public void test2(a) {
    Foo foo = new RealFoo();
    // Create an instance of the proxy class using the class loader, interface array, and call handler
    Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                                new Class[]{Foo.class},
                                                new MyInvocationHandler(foo));
    String value = fooProxy.ping("Little Dragon Lady");
    System.out.println(value);
}
Copy the code

2.5 by simplifying Lambda expressions

Instead of creating an implementation class, the InvocationHander interface can be simplified using Lambad expressions as follows:

@Test
public void test3(a) {
    Foo foo = new RealFoo();

    Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                                new Class[]{Foo.class},
                                                (proxy, method, args) -> method.invoke(foo, args));
    String value = fooProxy.ping("Brother diao");
    System.out.println(value);
}
Copy the code

3. Source code analysis

3.1 What does $Proxy look like

What exactly do the JVM automatically generate for us? So let’s just generate it, and then look at what’s inside.

3.1.1 Generating a. Class file for $Proxy

JVM default does not create it. The class files, need to add a launch parameters: – Dsun. Misc. ProxyGenerator. SaveGeneratedFiles = true

Click “Edit Configurations…” in the IDEA. Open the Run/Debug Configurations configuration box.

Add the above startup parameters to [VM Options] and click [OK].

If you run the code again, you will find the.class file in the [com.sun.proxy] directory of your project. In this case, it is “$proxy4.class”.

3.1.2 Why can $Proxy bytecode files be generated by adding this startup parameter

In the Proxy class, there is a ProxyClassFactory static inner class, which is used to generate static proxies.

There’s a code in thereProxyGenerator.generateProxyClassThe.class file used to generate the proxy class.

The variablesaveGeneratedFilesThe value of this startup parameter is referenced. Set the startup parameter totrueThe.class file is generated.

3.1.3 What does $Proxy look like

The mysterious veil is about to be revealed, the front of many unsolved mysteries can be found here!

Open the $Proxy file, I generated $Proxy4, here is the content:

// This class is final and inherits from Proxy and implements the proxied interface Foo
public final class $Proxy4 extends Proxy implements Foo {

    // These four Method instances represent the four methods implemented by this class
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    // Static code blocks get Method instances of these four methods based on reflection
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("io.github.gozhuyinglong.proxy.Foo").getMethod("ping");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw newNoClassDefFoundError(var3.getMessage()); }}// a public constructor with the specified InvocationHandler
    public $Proxy4(InvocationHandler var1) throws  {
        super(var1);
    }
    
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}public final String toString(a) throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}// Foo implements a method that ends up calling the Invoke method in InvocationHandler
    public final String ping(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}public final int hashCode(a) throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}}Copy the code

It can be seen from the file that:

  • The proxy class inheritsProxyClass, whose primary purpose is for deliveryInvocationHandler.
  • The proxy class implements the proxied interface Foo, which is why proxy classes can be directly enforced into interfaces.
  • There is an exposed constructor with the specified argumentsInvocationHandlerAnd pass the argument to the parent classProxyIn the.
  • Each implemented method is calledInvocationHandlerIn theinvokeMethod, and passes the proxy class itself, the Method instance, and the input parameter. This is why methods in a proxy class are always dispatched toInvocationHandlerIn theinvokeThe reason for the method.

3.2 How are proxy classes created

Let’s start with the two static methods that the Proxy class gives us: getProxyClass and newProxyInstance. As described above, these two methods are used to create proxy classes and their instances. Here is the source code.

3.2.1 getProxyClass and newProxyInstance methods

As you can see from the source code above, both methods eventually call getProxyClass0 to generate the Class object of the proxy Class. The newProxyInstance method creates the proxy instance for us, whereas the getProxyClass method requires us to create the proxy instance ourselves.

3.2.2 getProxyClass0 method

Here’s the unified entry:getProxyClass0

As can be seen from the source code and annotations:

  • The number of proxy interfaces cannot exceed 65535
  • The proxy class is first fetched from the cache and then not passedProxyClassFactoryCreate the proxy class. (Proxy classes are cached for a while.)

3.2.3 WeakCache class

Here is a brief introductionWeakCache<K, P, V> Class, which is primarily used to cache proxy classes. When a proxy class is fetched, it is first fetched from the cache, or called if it is notProxyClassFactoryClass is created and cached after it is created.

3.2.4 ProxyClassFactory class

ProxyClassFactory is a static inner class of the Proxy class that is used to generate the Proxy class. Here is part of the source code:

  • This is where the name of the proxy class is defined, prefixed with$ProxyThe suffix is a number.
  • callProxyGenerator.generateProxyClassTo generate the specified proxy class.
  • defineClass0The method is anativeMethod that is responsible for the implementation of bytecode loading and returns the correspondingClassObject.

The principle diagram of the 3.3

For documentation purposes, the generation of proxy classes is organized into a graph.

The source code to share

Complete code please visit my Github, if you are helpful, welcome to ⭐, thank you ~~🌹🌹🌹

Github.com/gozhuyinglo…

Recommended reading

  • Java reflection mechanism: Follow the code to learn reflection

About the author

project content
The public, AcmenStayUp (ID: AcmenStayUp)
The home page gozhuyinglong.github.io
CSDN Blog.csdn.net/gozhuyinglo…
tunneling Juejin. Cn/user / 123990…
Github Github.com/gozhuyinglo…
Gitee Gitee.com/gozhuyinglo…