Java Dynamic Agent Implementation and principle detailed analysis

Source: Internet
Author: User
Tags aop getmessage instance method throwable

On the dynamic agent in Java, we need to know first of all is a common design pattern-proxy mode, and for the agent, according to create proxy class point of time, but also can be divided into static agents and dynamic agents.

First, Agent mode

proxy mode is a common Java design pattern, his characteristic is that the proxy class and the delegate class have the same interface, the proxy class is mainly responsible for the delegate class preprocessing messages, filtering messages, forwarding messages to the delegate class, and post-processing messages. There is usually an association between the proxy class and the delegate class, and the object of a proxy class is associated with an object of a delegate class, and the object of the proxy class does not actually implement the service, but instead provides a specific service by invoking the related method of the object of the delegate class. Simply put, when we access the actual object, it is accessed through the proxy object, the proxy mode is to introduce a degree of indirection when accessing the actual object, because this kind of indirection can be attached to many purposes. In the back I will

Explain the benefits of this indirection. Proxy mode structure diagram (picture from "Big talk Design Mode"):

Second, static agent

1. Static Agent

Static proxy: Created by the programmer or a specific tool to automatically generate the source code, that is, at compile time, the interface, the proxy class, proxy class, etc. determined. Before the program runs, the. class file for the proxy classes is already generated.

2, static proxy simple implementation

Write an example of a simple static proxy based on the class diagram of the proxy pattern above. I am here to give a more rough example, if a class of students to the teacher to shift fees, but all through the monitor to the teacher to transfer their money. Here, the monitor is the agent of the students on the handover fee,

The monitor is the student's agent.

First, we create a person interface. This interface is the student (the Agent Class), and the monitor (proxy class) of the public interface, they all have the act of the handover fee. In this way, the students to the handover fee can let the monitor to act as agent.

/** * Create Person interface * @author Gonjan */public interface Person {    //on handover fee    void Givemoney ();}

The student class implements the person interface. Student can be specifically implemented on the shift fee action.

public class Student implements person {    private String name;    Public Student (String name) {        this.name = name;    }        @Override public    void Givemoney () {       System.out.println (name + "50 yuan on handover fee");}    }

Studentsproxy class, this class also implements the person interface, but also holds a student class object, because implements the Peson interface, simultaneously holds a student object, then he can delegate student class object to carry on the shift fee (executes the Givemoney () method) behavior.

/** * Student Agent class, also implements the person interface, save a student entity, so that can act as a student to create behavior * @author Gonjan * */public class Studentsproxy implements person{    // The student who is represented    Student Stu;        Public Studentsproxy (Person Stu) {        //Proxy Student Object        if (stu.getclass () = = Student.class) {            This.stu = (Student) stu ;        }    }        Agent on the handover fee, the call was the agent of the students Banfei behavior public    void Givemoney () {        Stu.givemoney ();    }}

Here's a test to see how to use the proxy mode:

public class Staticproxytest {public    static void Main (string[] args) {        //The student who was represented Zhang San, his Banfei handed in a proxy object monitor (monitor) completed Person        Zhangsan = new Student ("Zhang San");                Generates a proxy object and passes Zhang San to the proxy object person        monitor = new Studentsproxy (Zhangsan);                Monitor Agent on the handover fee        Monitor.givemoney ();    }}

Operation Result:

Here is not directly through the Zhang San (the agent object) to carry out the act of the handover fee, but through the monitor (proxy object) to perform the agent. This is the proxy mode.

The main thing about proxy mode is that there is a public interface (person), a specific class (Student), a proxy class (Studentsproxy), and the proxy class holds an instance of the concrete class, and executes the concrete class instance method on its behalf. As mentioned above, the proxy mode is to introduce some degree of indirection when accessing the actual object, because this kind of indirection can attach many kinds of purposes. The indirection here refers to the method that does not invoke the actual object directly, then we can add some other uses in the agent process. In this case, join the monitor to help Zhang San hand over Banfei before you want to reflect the Zhang San recent study has great progress, through the proxy mode is easy to do:

public class Studentsproxy implements person{    //The student who is represented    Student Stu;        Public Studentsproxy (Person Stu) {        //Proxy Student Object        if (stu.getclass () = = Student.class) {            This.stu = (Student) stu ;        }    }        Agent on the handover fee, call the Banfei behavior of the students handed over to public    void Givemoney () {        System.out.println ("Zhang San recent learning has progressed! ");        Stu.givemoney ();    }}

Operation Result:

As you can see, you just need to do other things before you hand over the Banfei in the proxy class Zhang San. This operation is also a great advantage of using the proxy mode. The obvious thing is that aspect-oriented programming (AOP) in spring allows us to perform some operations before a tangent point, which is a method of doing something after a tangent point. The class of these methods is definitely the agent, in the agent process to cut into some other operations.

Third, dynamic agent

1. Dynamic Agent

proxy classes are created as dynamic proxies when the program is run. In our example of static proxies above, the proxy class (STUDENTPROXY) is defined by itself and is compiled before the program runs. However, dynamic proxies, proxy classes are not defined in Java code, but are dynamically generated at runtime based on our "instructions" in Java code. Compared to static proxies, the advantage of dynamic proxies is that the functions of the proxy class can be handled uniformly, without modifying the methods in each proxy class. For example, you want to add a processing method before each agent's method:

public void Givemoney () {        //Call the Proxy method before adding the processing method        Beforemethod ();        Stu.givemoney ();    }

Here is only a Givemoney method, write a Beforemethod method, but if out of Givemonney there are many other methods, it will need to write many times Beforemethod method, trouble. Let's see how the dynamic agent is implemented below.

2, dynamic agent simple implementation

A proxy class and a Invocationhandler interface are provided under the Java Java.lang.reflect package to generate the JDK dynamic proxy class and the dynamic proxy object through this class and interface.

To create a dynamic proxy object step, see the following code:

    • Create a Invocationhandler object
Create a invocationhandler Invocationhandler Stuhandler = new Myinvocationhandler<person> (stu) associated with the proxy object;

    • Use the Getproxyclass static method of the proxy class to generate a dynamic proxy class Stuproxyclass
  class<?> Stuproxyclass = Proxy.getproxyclass (Person.class.getClassLoader (), new class<?>[] {Person.class });

    • Get a constructor with Invocationhandler parameter in Stuproxyclass constructor
constructor<?> Constructor = Personproxy.getconstructor (Invocationhandler.class);

    • Create a dynamic instance from the constructor constructor Stuproxy
Person stuproxy = (person) cons.newinstance (Stuhandler);

In this regard, a dynamic proxy object is created, of course, the above four steps can be simplified by the proxy class Newproxyinstances method:

Create a invocationhandler  invocationhandler stuhandler = new Myinvocationhandler<person> (stu) associated with the proxy object;// Creates a proxy object Stuproxy, each execution method of the proxy object replaces the Invoke method in the execution invocation person  stuproxy= (person) proxy.newproxyinstance ( Person.class.getClassLoader (), New Class<?>[]{person.class}, Stuhandler);

Will certainly be very puzzled here, how this dynamic agent is executed, is how to execute the proxy object through the proxy object method, first of all, we first look at a simple complete dynamic proxy example. Or the above static proxy example, the monitor needs to help students on behalf of the handover fee.
The first is to define a person interface:

/** * Create Person interface * @author Gonjan */public interface Person {    //on handover fee    void Givemoney ();}

Create the actual class that needs to be proxied:

public class Student implements person {    private String name;    Public Student (String name) {        this.name = name;    }        @Override public    void Givemoney () {        try {          //assumes that the sum of money takes a second time            thread.sleep ()        } catch ( Interruptedexception e) {            e.printstacktrace ();        }       SYSTEM.OUT.PRINTLN (name + "50 yuan on the handover fee");}    }        

Then define a tool class to detect the execution time of the method, call the Start method before any method executes, then call the Finsh method after execution to calculate the running time of the method, which is also the simplest method to execute time detection tool.

public class Monitorutil {        private static threadlocal<long> tl = new threadlocal<> ();        public static void Start () {        tl.set (System.currenttimemillis ());    }        At the end of the print time-consuming public    static void finish (String methodName) {        Long finishtime = System.currenttimemillis ();        System.out.println (MethodName + "method time-consuming" + (Finishtime-tl.get ()) + "MS");}    }

Create the Stuinvocationhandler class to implement the Invocationhandler interface, which holds an instance target of the Proxied object. There is an Invoke method in Invocationhandler, and all methods to execute the proxy object are replaced with the Invoke method.

The corresponding method to execute the target of the proxy object in the Invoke method again. Of course, during the agent process, we add our own other processing before actually executing the method of the Proxied object. This is also the main principle of the implementation of AOP in spring, which also involves a very important basic knowledge of Java reflection.

public class Stuinvocationhandler<t> implements Invocationhandler {   //invocationhandler held by proxy object    T Target;        Public Stuinvocationhandler (T target) {       this.target = target;    }        /**     * Proxy: Represents the dynamic proxy object     * Method: Represents the method being executed     * args: Represents the argument that was passed in when the target method was called    */@Override public Object Invoke (Object Proxy, Method method, object[] args) throws Throwable {        System.out.println ("Proxy Execution" +method.getname () + "Method");     *           ///Agent process in the insertion of monitoring methods, calculation of the method time        -consuming monitorutil.start ();        Object result = Method.invoke (target, args);        Monitorutil.finish (Method.getname ());        return result;}    }

After doing the above work, we can specifically create dynamic proxy objects, which briefly describes how to create dynamic proxy objects, we use a simplified way to create dynamic proxy objects:

public class Proxytest {public    static void Main (string[] args) {                //Create an instance object, which is the object being proxied        Zhangsan = NE W Student ("Zhang San");                Create a invocationhandler        invocationhandler stuhandler = new stuinvocationhandler<person> associated with the proxy object ( Zhangsan);                Create a proxy object Stuproxy to proxy Zhangsan, each execution method of the proxy object replaces the Invoke method in the execution invocation person        stuproxy = (person) Proxy.newproxyinstance (Person.class.getClassLoader (), New Class<?>[]{person.class}, Stuhandler);       The agent executes the method of        Stuproxy.givemoney ();    }}

We execute this proxytest class, first think about, we created a need to be represented by the student Zhang San, Zhangsan object to the Stuhandler, we create the proxy object Stuproxy, Stuhandler as a parameter, It also says that all the methods that execute the proxy object are replaced with the Invoke method, that is, the Invoke method in Stuinvocationhandler is executed last. So it's a matter of course to see the results below.

Operation Result:

As mentioned above, the advantage of the dynamic agent is that it is convenient to handle the functions of the proxy class uniformly, without modifying the methods in each proxy class. Because all of the methods that are executed by the agent are called by the Invoke method in Invocationhandler, so we can do the same thing for all the proxied methods as long as we work uniformly in the Invoke method. For example, the method here is timed, and all the methods executed by the proxy object will be timed, but I've only done a very small amount of code.

The process of the dynamic agent, the relationship between the proxy object and the Proxied object is not as clear as the static proxy. Because of the dynamic agent process, we do not actually see the proxy class, and do not clearly see the exact appearance of the proxy class, and the dynamic agent in the proxy object and proxy object is through the Invocationhandler to complete the agent process, which is how the specific operation, The method that the proxy object executes is executed by the Invoke method in Invocationhandler. With these questions, we need to make a brief analysis of the source code of the Java Dynamic Agent, and find out why.

Four, dynamic agent principle Analysis

1. Dynamic proxy class created by Java dynamic Agent

Above we use the proxy class Newproxyinstance method to create a dynamic proxy object, look at the source of the method, found that it just encapsulated the creation of a dynamic proxy class (Red standard section):

public static Object newproxyinstance (ClassLoader loader, class<?>[] Interf Aces, Invocationhandler H) throws IllegalArgumentException {OBJ        Ects.requirenonnull (h);        Final class<?>[] Intfs = Interfaces.clone ();        Final SecurityManager sm = System.getsecuritymanager ();        if (SM! = null) {checkproxyaccess (Reflection.getcallerclass (), loader, intfs);         }/* * Look up or generate the designated proxy class. */class<?> cl = getProxyClass0 (loader, intfs); /* * Invoke its constructor with the designated invocation handler.            */try {if (SM! = null) {checknewproxypermission (Reflection.getcallerclass (), CL);            } final Constructor<?> cons = Cl.getconstructor (constructorparams);            Final Invocationhandler ih = h; if (!                    Modifier.ispublic (Cl.getmodifiers ())) {accesscontroller.doprivileged (new privilegedaction<void> () {                        Public Void Run () {cons.setaccessible (true);                    return null;            }                });        } return Cons.newinstance (new object[]{h}); } catch (illegalaccessexception|        Instantiationexception e) {throw new Internalerror (E.tostring (), E);            } catch (InvocationTargetException e) {throwable t = e.getcause (); if (t instanceof RuntimeException) {throw (runtimeexception) t;            } else {throw new Internalerror (t.tostring (), t);        }} catch (Nosuchmethodexception e) {throw new Internalerror (E.tostring (), E); }    }

In fact, the most we should pay attention to is class<?> CL = GETPROXYCLASS0 (loader, intfs); This is where the proxy class is generated, and the constructor in the following code is obtained by the class generated here, as you can see, The generation of this class is the key to the entire dynamic agent, because it is a dynamically generated class file, I do not specifically enter the analysis of how to produce this class file, only need to know that the class file is slow to exist in the Java Virtual machine, we can use the following method to print it into the file, a glimpse of tolerance distortion:

        byte[] Classfile = Proxygenerator.generateproxyclass ("$Proxy 0", Student.class.getInterfaces ());        String Path = "G:/javacode/javase/test/bin/proxy/stuproxy.class";        Try (fileoutputstream fos = new FileOutputStream (path)) {            fos.write (classfile);            Fos.flush ();            SYSTEM.OUT.PRINTLN ("Agent class file writes successfully");        } catch (Exception e) {           System.out.println ("Write file Error");        }

To decompile this class file, let's look at what the JDK has generated for us:

Import Java.lang.reflect.invocationhandler;import Java.lang.reflect.method;import Java.lang.reflect.proxy;import Java.lang.reflect.undeclaredthrowableexception;import Proxy.  Person;public Final class $Proxy 0 extends Proxy implements person{private static Method M1;  private static Method m2;  private static Method m3;    private static Method M0; /** * Note Here is the constructor of the build proxy class, the method parameter is the Invocationhandler type, see this, is not a bit clear * Why the proxy object call method is to execute the Invoke method in Invocationhandler, And Invocationhandler hold an instance of a proxy object, can not help but think is ...?  Yes, that's what you think.  * *super (Paraminvocationhandler), is the constructor method that calls the parent class proxy.  * Parent class Holds: protected Invocationhandler h;  *proxy Construction Method: * Protected Proxy (Invocationhandler h) {* Objects.requirenonnull (h);  * This.h = h;  *} * */public $Proxy 0 (Invocationhandler Paraminvocationhandler) throws {super (Paraminvocationhandler); }//This static block was originally in the last, I took it to the front, convenient to describe static {try {///See what is inside the static block, is not found the Givemoney method. Remember Givemoney the name M3 by reflection, the other first regardless of 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 ("Proxy.      Person "). GetMethod (" Givemoney ", new Class[0]);      M0 = Class.forName ("Java.lang.Object"). GetMethod ("Hashcode", new Class[0]);    Return } catch (Nosuchmethodexception localnosuchmethodexception) {throw new Nosuchmethoderror (LOCALNOSUCHMETHODEXCEP    Tion.getmessage ()); } catch (ClassNotFoundException localclassnotfoundexception) {throw new Noclassdeffounderror (Localclassnotfoun    Dexception.getmessage ());  }}/** * * This invokes the Givemoney method of the proxy object, calls the Invoke method in the Invocationhandler directly, and passes the M3 in.  *this.h.invoke (this, M3, null); it's simple and clear here. * Come on, think again, the proxy object holds a Invocationhandler object, the Invocationhandler object holds a Proxied object, and the Invoke method in the Invacationhandler is contacted.  Well, that's it.      */public final void Givemoney () throws {try {This.h.invoke (this, M3, null);    Return } catch (error|    RuntimeException localerror) {throw localerror;    } catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable); }}//Note that in order to save space, the contents of the Tostring,hashcode and Equals methods are omitted. The principle and the Givemoney method are like a hair. }

The JDK generates a proxy class called $PROXY0 (0 after this name is numbered, with multiple proxy classes incremented at a time), and this class file is placed in memory, and when we create the proxy object, we get the constructor of the class by reflection and then create the proxy instance. Through the generation of proxy class source of the view, we can easily see, dynamic agent implementation of the specific process.

We can treat Invocationhandler as a mediation class, which holds a proxy object and invokes the corresponding method of the Proxied object in the Invoke method. By aggregating a reference to the proxy object, the external call to invoke is eventually converted to the call to the Proxied object.

When the proxy class calls its own method, the Invoke method of the Mediation class object is invoked through the mediation class object held by itself, thus reaching the method by which the agent executes the Proxied object. In other words, the dynamic agent realizes the specific proxy function through the intermediary class.

V. Summary

Generated proxy class: $Proxy 0 extends proxy implements person, we see that the proxy class inherits the proxies class, so it is decided that the Java Dynamic Agent can only proxy the interface, Java's inheritance mechanism is doomed to these dynamic proxy classes can not implement the dynamic proxy class.
The example of dynamic proxy above is actually a simple implementation of AOP, which is processed before and after the method of the target object is executed, and the method time is counted. Spring's AOP implementation is actually using proxy and invocationhandler these two things.

After the supplement, know it, but also know why.

Java Dynamic Agent Implementation and principle detailed analysis

Related Article

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.