The implementation and principle of JDK dynamic Agent in Java Beauty [from rookie to expert walkthrough]

Source: Internet
Author: User

implementation and principle of JDK dynamic agent

Two cyan

Email: [Email protected] Weibo: HTTP://WEIBO.COM/XTFGGEF

Dynamic agent, sounds very big on the technology, in the Java application of a wide range, especially in the hibernate and spring of the two frameworks, in the AOP, permissions control, transaction management and other aspects of the implementation of dynamic agents. The JDK itself implements dynamic proxy technology, but there is a slight limitation that the class being proxied must implement an interface, otherwise it cannot use the dynamic proxy that comes with the JDK, so if the condition is not met, it can only use another more flexible, more powerful dynamic agent technology--cglib. Spring automatically switches between the JDK's proxy and cglib, and we can force spring to use Cglib. Here we have a dynamic agent of knowledge points from beginning to the next introduction.

Let's first look at an example:

Create a new interface, Userservice.java, with only one method add ().

Package Com.adam.java.basic;public interface UserService {public abstract void Add ();}

Build an implementation class for this interface Userserviceimpl.java

Package Com.adam.java.basic;public class Userserviceimpl implements UserService {@Overridepublic void Add () { SYSTEM.OUT.PRINTLN ("-----Add-----");}}

Build an agent processing class Myinvocationhandler.java

Package Com.adam.java.basic;import Java.lang.reflect.invocationhandler;import Java.lang.reflect.method;import Java.lang.reflect.proxy;public class Myinvocationhandler implements Invocationhandler {private Object Target;public Myinvocationhandler (Object target) {super (); this.target = target;} Public Object GetProxy () {return proxy.newproxyinstance (Thread.CurrentThread (). Getcontextclassloader (), Target.getclass (). Getinterfaces (), this);} @Overridepublic object Invoke (Object proxy, Method method, object[] args) throws Throwable {System.out.println ("----- Before-----"); Object result = Method.invoke (target, args); SYSTEM.OUT.PRINTLN ("-----After-----"); return result;}}

Test class

Package Com.adam.java.basic;public class Dynamicproxytest {public static void main (string[] args) {UserService UserService = new Userserviceimpl (); Myinvocationhandler Invocationhandler = new Myinvocationhandler (userservice); UserService proxy = (userservice) invocationhandler.getproxy ();p roxy.add ();}}

Execute the test class and get the following output:
-----before-----
-----Add-----
-----after-----
Here, we should think of something:
1. Who and how is this proxy object generated?
2. How is the Invoke method called?
3. What is the corresponding relationship between the Invoke and add methods?
4. What does the generated proxy object look like?
With these questions, let's look at the source code. First of all, our entrance is the GetProxy () method in the test class above, let's go in and look at this method:

Public Object GetProxy () {return proxy.newproxyinstance (Thread.CurrentThread (). Getcontextclassloader (), Target.getclass (). Getinterfaces (), this);}

In other words, the dynamic agent of the JDK is implemented by a class called Proxy, and we continue to follow it to see the Newproxyinstance () method of the proxy class. Let's take a look at the JDK notes:

/**     * Returns An instance of a proxy class for the specified interfaces     * This dispatches method invocations to th E specified invocation     * handler.     *     * <p>{@code proxy.newproxyinstance} throws     * {@code illegalargumentexception} for the same reasons that< c6/>* {@code Proxy.getproxyclass} does.     *     * @param   Loader the class loader to define the proxy class     * @param   interfaces The list of interfaces For the proxy class     * to          implement     * @param   h the invocation handler to dispatch method invocations to     * @return  a proxy instance with the specified invocation handler of a     *          proxy class This is defined B Y the specified class loader     * And that          implements the specified interfaces

According to the JDK note, we know that the Newproxyinstance method will eventually return an instance of the class that implements the specified interface, with three parameters: ClassLoader, the specified interface, and our own defined Invocationhandler class. I picked out some key code to see how the instance object of the proxy class was generated.

class<?> cl = GETPROXYCLASS0 (loader, intfs); final constructor<?> cons = Cl.getconstructor ( Constructorparams);... return cons.newinstance (New object[]{h});

Interested students can see their own JDK source code, the current version I use is JDK1.8.25, each version of the implementation may not be the same, but basically consistent, please study the source of the students note this point. The above code shows that the proxy class is obtained first through Getproxyclass, and then the constructor is taken through C1.getconstructor (), the last step, Returning an instance of this new proxy class via cons.newinstance, note: When calling newinstance, the passed parameter is H, which is our own definition of the Invocationhandler class, first remember this step, we will know the reason for doing this here.

In fact, these three code, the core is this getproxyclass method, the other two lines of code is the application of Java reflection, and our current point of interest is not related, so we continue to study the Getproxyclass method. This method, the annotation is very simple, as follows:

/         * Look up or generate the designated proxy class.         *        /class<?> cl = GETPROXYCLASS0 (loader, intfs);

is to generate this key proxy class, we follow up to see.

private static class<?> GetProxyClass0 (ClassLoader loader,                                           class<?> .... interfaces) {        if ( Interfaces.length > 65535) {            throw new IllegalArgumentException ("Interface limit Exceeded");        }        If the proxy class defined by the given loader implementing        //The given interfaces exists, this would simply return The cached copy;        Otherwise, it'll create the proxy class via the Proxyclassfactory        return Proxyclasscache.get (loader, interfaces);    }

This is used in the cache, first check from the cache, if present, directly returned, does not exist on the newly created. In this get method, we see the following code:
Object subkey = Objects.requirenonnull (Subkeyfactory.apply (key, parameter));
This refers to apply (), which is the inner class of the proxy class proxyclassfactory a method to implement its interface, the implementation is as follows:

Public class<?> Apply (ClassLoader loader, class<?>[] interfaces) {            map<class<?>, boolean> Interfaceset = new identityhashmap<> (interfaces.length);            for (class<?> intf:interfaces) {                /                 * * Verify that the Class loader resolves the name of this                 * Interfa Ce to the same Class object.                 */                class<?> interfaceclass = null;                try {                    Interfaceclass = Class.forName (Intf.getname (), false, loader);                } catch (ClassNotFoundException e) {                }                if (interfaceclass! = intf) {                    throw new IllegalArgumentException (                        intf + "is no visible from class load Er ");                } ...

When I see Class.forName (), I think most people will laugh and finally see the familiar way, right! This place is to load the specified interface, since the class is generated, then there will be a corresponding class bytecode, we continue to look down:

/* * Generate the specified proxy class. *   /byte[] Proxyclassfile = Proxygenerator.generateproxyclass (   proxyname, interfaces, accessflags);    try {          return defineClass0 (loader, proxyname,          proxyclassfile, 0, proxyclassfile.length);

This code is the use of proxygenerator for us to generate the final proxy class bytecode file, that is, the GetProxyClass0 () method of the final return value. So let's review the first four questions:

1. Who and how is this proxy object generated?

2. How is the Invoke method called?

3. What is the corresponding relationship between the Invoke and add methods?

4. What does the generated proxy object look like?

For the first question, I think the answer is clear, I repeat the idea: the proxy class of the GETPROXYCLASS0 () method to generate the target proxy class, and then get the construction method of the class, and finally through the reflection of the Newinstance method, the proxy class of the instance object.

Next, we look at the other three methods, I want to start with the fourth, because with the code generated above the bytecode, then we can imitate this step, we generate a bytecode file to see, so, I use the following code, generated this final proxy class.

Package Com.adam.java.basic;import Java.io.fileoutputstream;import Java.io.ioexception;import Sun.misc.proxygenerator;public class Dynamicproxytest {public static void main (string[] args) {UserService UserService = New Userserviceimpl (); Myinvocationhandler Invocationhandler = new Myinvocationhandler (userservice); UserService proxy = (userservice) invocationhandler.getproxy ();p roxy.add (); String Path = "c:/$Proxy 0.class"; byte[] Classfile = Proxygenerator.generateproxyclass ("$Proxy 0", UserServiceImpl.class.getInterfaces ()); FileOutputStream out = null;try {out = new FileOutputStream (path); Out.write (classfile); Out.flush ();} catch (Exception e) {E.printstacktrace ();} Finally {try {out.close ();} catch (IOException e) {e.printstacktrace ()}}}}

The Proxy.add () in the test method above, the Add () method here, is not the Add () method in the original UserService, but the Add () method of the newly generated proxy class, we will generate the $ Proxy0.class file opened with Jd-gui, I removed some code, the Add () method is as follows:

Public final void Add ()    throws   {    try    {      This.h.invoke (this, M3, null);      return;    }    catch (error| RuntimeException localerror)    {      throw localerror;    }    catch (Throwable localthrowable)    {      throw new undeclaredthrowableexception (localthrowable);    }  }

The core is this.h.invoke (this. m3, null); what's H here? Let's look at the class name of this class:

Public final class $Proxy 0 extends Proxy implements UserService

It is not difficult to find that the newly generated class, inherits the proxy class to implement the UserService method, and this userservice is our designated interface, so, here we can basically conclude that the JDK dynamic agent, the generation of the new proxy class is inherited proxy base class, The class that implements the incoming interface. What exactly is this h? Let's look at this new proxy class and see the constructor:

Public $Proxy 0 (Invocationhandler Paraminvocationhandler)    throws   {    super (Paraminvocationhandler);  }

The constructor passed in a parameter of type Invocationhandler, see here, we should think of the previous line of code:

Return cons.newinstance (New object[]{h});

This is the last sentence of the Newinstance method, the incoming H, which is the H used here, which is the instance of the Myinvocationhandler class that we originally defined. So, we found that the last call to the Add () method actually calls the Myinvocationhandler invoke () method. Let's look at this method again, look for the meaning of M3, continue to look at the source of proxy class:

Static  {    Try    {      m1 = Class.forName ("Java.lang.Object"). GetMethod ("equals", new class[] { Class.forName ("Java.lang.Object")});      M2 = Class.forName ("Java.lang.Object"). GetMethod ("ToString", new Class[0]);      M3 = Class.forName ("Com.adam.java.basic.UserService"). GetMethod ("Add", new Class[0]);      M0 = Class.forName ("Java.lang.Object"). GetMethod ("Hashcode", new Class[0]);      return;    }

Surprise discovery, originally this M3, is the original interface Add () method, see here, what do not understand? I think the 2,3,4 problem should be solved, right? Let's go ahead and look at the Invoke () method in the original Myinvocationhandler:

@Overridepublic object Invoke (Object proxy, Method method, object[] args) throws Throwable {System.out.println ("----- Before-----"); Object result = Method.invoke (target, args); SYSTEM.OUT.PRINTLN ("-----After-----"); return result;}

M3 is the method that will be passed in, so why first output before, after output after, to here is not fully understood? This is the whole process of the dynamic agent of the JDK, isn't it difficult?

Finally, let me summarize the operation process of the JDK dynamic agent:

1. Define an interface where there are methods that need to be implemented, and write the actual implementation class.

2. Define a Invocationhandler class, implement the Invocationhandler interface, override the Invoke () method, and add the GetProxy () method.

Summarize the dynamic agent implementation process:

1. Generate the proxy class through GETPROXYCLASS0 ().

2. The instance object of the proxy class is generated by Proxy.newproxyinstance (), and an instance of the Invocationhandler type is passed in when the object is created.

3. Call the new instance's method, which is the add () in this example, which is the Invoke () method in the original Invocationhandler class.

Well, write so much, also should end, thank Bo Friend Rejoy an article, let me have a reference. At the same time welcome everyone to discuss the question, if there are questions, please leave a message, I will take the time to reply. The relevant code has been uploaded to Baidu network disk,.

Contact information:

Email: [Email protected]

Weibo: HTTP://WEIBO.COM/XTFGGEF


    • Reference: http://rejoy.iteye.com/blog/1627405

The implementation and principle of JDK dynamic Agent in Java Beauty [from rookie to expert walkthrough]

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.