Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is a JDK dynamic Agent

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what is a JDK dynamic agent". Friends who are interested may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what is a JDK dynamic agent"?

JDK dynamic proxy refers to the dynamic generation of proxy class instances by JVM according to the reflection mechanism when the program is running. That is to say, the proxy class is not defined by the user, but generated by JVM.

Because its principle is realized 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 reflection

Here is the content of this article:

1. The core class of JDK dynamic agent

The JDK dynamic proxy has two core classes, both of which are under the Java reflection package (java.lang.reflect), namely the InvocationHandler interface and the Proxy class.

1.1 InvocationHandler interface

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

That is, each proxy instance we create will have an associated InvocationHandler, and when the method of the proxy instance is called, it will be transferred to the invoke method of InvocationHandler.

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

The invoke method handles the method call on the proxy instance and returns the result.

It has three parameters, which are:

Proxy: is the proxy instance that calls this method.

Method: is the Method instance corresponding to the interface method called on the proxy instance.

Args: an Object array that is the parameter value passed in a method call on the proxy instance. If the interface method is no parameter, the value is null.

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

1.2 Proxy class

The Proxy class provides static methods for creating a dynamic proxy class and its instances, which is also a superclass of the dynamic proxy class.

The proxy class has the following properties:

The name of the proxy class begins with "$Proxy", followed by a numeric sequence number.

The proxy class inherits the Proxy class.

The proxy class implements the interface specified at creation time (the JDK dynamic proxy is interface-oriented).

Each proxy class has a common constructor that accepts one parameter, the implementation of the interface InvocationHandler, to set the call handler for the proxy instance.

Proxy provides two static methods for getting the proxy object.

1.2.1 getProxyClass

Used to get the Class object of the proxy class, and then create a proxy instance by calling the constructor.

Public static Class getProxyClass (ClassLoader loader, Class... Interfaces) throws IllegalArgumentException

This method has two parameters:

Loader: class loader.

Intefaces: an array of Class objects for the interface.

The return value is the Class object of the dynamic proxy class.

1.2.2 newProxyInstance

Used to create a proxy instance.

Public static Object newProxyInstance (ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException

This method has three parameters:

Loader: class loader.

Interfaces: an array of Class objects for the interface.

H: the specified call handler.

The return value is 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.

two。 Get the code example of the agent instance

Two static methods for getting a proxy instance have been introduced in the previous chapter, and now a code example is used to demonstrate the implementation.

2.1 create the target interface and its implementation class

The JDK dynamic proxy is interface-based, and we create an interface and its implementation class.

Foo interface:

Public interface Foo {String ping (String name);}

The implementation class RealFoo of the Foo interface:

Public class RealFoo implements Foo {@ Override public String ping (String name) {System.out.println ("ping"); return "pong";}} 2.2 create 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 three parameters in the invoke method are described above, and the method is called by calling the invoke method of method.

It doesn't matter if I don't understand it for a while, but I will analyze it in the following source code analysis chapter.

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)); return method.invoke (target, args);}} 2.3.Mode 1: obtain the proxy instance through the getProxyClass method

The specific implementation steps are as follows:

Get the Class object of the proxy class according to the class loader and interface array

Create an instance (an instance of the proxy class) through the constructor of the Class object

Forcibly convert the proxy instance to the target interface Foo (because the proxy class implements the target interface, you can).

Finally, the proxy is used to make the method call.

@ Testpublic void test1 () throws Exception {Foo foo = new RealFoo (); / / get the Class object of the proxy class Class proxyClass = Proxy.getProxyClass (Foo.class.getClassLoader (), Foo.class) based on the classloader and interface array; / / create an instance (instance of the proxy class) Foo fooProxy = (Foo) proxyClass.getConstructor (InvocationHandler.class) .newInstance (new MyInvocationHandler (foo)) through the constructor of the Class object / / call the ping method and output the return value String value = fooProxy.ping ("Yang Guo"); System.out.println (value);}

Output result:

Proxy-class com.sun.proxy.$Proxy4method-public abstract java.lang.String io.github.gozhuyinglong.proxy.Foo.ping (java.lang.String) args-[Yang Guo] pingpong

The output results can be seen as follows:

The name of the proxy class starts with $Proxy.

The method instance is the method called by the proxy class.

The parameter is the parameter passed when the proxy class invokes the method.

Method 2: obtain the proxy instance through the newProxyInstance method

This method is the simplest and recommended, and the proxy object can be obtained directly by this method.

Note: in fact, the background implementation of this method is actually the same as using the getProxyClass method above.

@ Testpublic void test2 () {Foo foo = new RealFoo () / create an instance of the proxy class Foo fooProxy = (Foo) Proxy.newProxyInstance (Foo.class.getClassLoader (), new Class [] {Foo.class}, new MyInvocationHandler (foo)) through the class loader, interface array and call processor; String value = fooProxy.ping ("Little Dragon Girl") System.out.println (value);} 2.5 is implemented through simplified Lambda expressions

In fact, instead of creating an implementation class for the InvocationHander interface, you can use Lambad expressions to simplify the implementation, as shown in the following code:

@ Testpublic void test3 () {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); 3. Source code parsing 3.1 what does the proxy class $Proxy look like

What exactly does the proxy class that JVM automatically generates for us look like? Let's first generate it, and then look at the structure inside.

3.1.1 generate a .class file for $Proxy

JVM does not create the .class file by default. You need to add a startup parameter:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

Click [Edit Configurations...] in IDEA to open the Run/Debug Configurations configuration box.

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

Run the code again, and you will find this .class file in the [com.sun.proxy] directory in the project. Here is "$Proxy4.class".

3.1.2 Why can the bytecode file of $Proxy be generated by adding this startup parameter?

There is a ProxyClassFactory static inner class in the Proxy class, which is mainly used to generate static proxies.

There is a piece of code ProxyGenerator.generateProxyClass that is used to generate the .class file of the proxy class.

The variable saveGeneratedFiles refers to the value of this startup parameter. Configuring this startup parameter to true generates a .class file.

3.1.3 what exactly does this proxy class $Proxy look like

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

Open this $Proxy file, and what I generate here is $Proxy4. Here is the content:

/ / this class is the final class, which inherits the Proxy class and implements the four Method instances of the proxied interface Foopublic final class $Proxy4 extends Proxy implements Foo {/ /, representing the four methods private static Method M1; private static Method m2; private static Method m3; private static Method M0 implemented by this class. / / the static code block gets the Method instances of the four methods static {try {M1 = Class.forName ("java.lang.Object"). GetMethod ("equals", Class.forName ("java.lang.Object")) based on reflection; 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 new NoClassDefFoundError (var3.getMessage ()) }} / / A public constructor with arguments to 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 new UndeclaredThrowableException (var4);}} public final String toString () throws {try {return (String) super.h.invoke (this, m2, (Object []) null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException (var3) Finally, the invoke method public final String ping (String var1) throws {try {return (String) super.h.invoke (this, m3, new Object [] {var1}) in InvocationHandler is called;} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException (var4) } public final int hashCode () throws {try {return (Integer) super.h.invoke (this, M0, (Object []) null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException (var3);}

From this file, you can see:

The proxy class inherits the Proxy class, and its main purpose is to pass InvocationHandler.

The proxy class implements the proxied interface Foo, which is why the proxy class can be forcibly converted directly to the interface.

There is an exposed constructor that takes the specified InvocationHandler and passes the parameter to the parent class Proxy.

Each implemented method calls the invoke method in InvocationHandler and passes three parameters: the proxy class itself, the Method instance, and the input parameter. This is why when methods in a proxy class are called, they are always dispatched to the invoke method in InvocationHandler.

3.2 how is the proxy class created

Let's start with getProxyClass and newProxyInstance with the two static methods provided to us by the proxy class. As described above, these two methods are used to create proxy classes and their instances. Let's take a look at the source code.

3.2.1 getProxyClass and newProxyInstance methods

As you can see from the source code above, both methods will eventually call the getProxyClass0 method to generate the Class object of the proxy class. It's just that the newProxyInstance method creates the proxy instance for us, while the getProxyClass method requires us to create the proxy instance ourselves.

3.2.2 getProxyClass0 method

Let's take a look at this unified entrance: getProxyClass0

As can be seen from the source code and comments:

The maximum number of proxy interfaces is 65535.

The proxy class is first obtained from the cache, then the proxy class is no longer created through ProxyClassFactory. (the proxy class will be cached for a while. )

3.2.3 WeakCache Class

Here's a brief introduction to the WeakCache class, which is mainly cached for proxy classes. When a proxy class is obtained, it will first be obtained from the cache. If no ProxyClassFactory class is called to create it, it will be cached after it is created.

3.2.4 ProxyClassFactory class

ProxyClassFactory is a static inner class of the Proxy class, which is used to generate proxy classes. The following figure is part of the source code:

The name of the proxy class is defined here with a prefix of $Proxy and a suffix of a number.

Call ProxyGenerator.generateProxyClass to generate the specified proxy class.

The defineClass0 method is a native method that is responsible for the implementation of bytecode loading and returns the corresponding Class object.

3.3 schematic

In order to facilitate recording, the generation process of the proxy class is organized into a diagram.

At this point, I believe you have a deeper understanding of "what is a JDK dynamic agent". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report