"Reprint" Java dynamic agent

Source: Internet
Author: User

Java Dynamic Agent

This article is for Android Open source project source code to parse the dynamic agent part of public technical point
Project Address: Jave Proxy, analyzed version: OpenJDK 1.6,demo address: Proxy Demo
Analyst: Caij, Reviewer: trinea, proofreading Status: complete

1. Related Concepts 1.1 agents

In some cases, we do not want or can not directly access object A, but by accessing a mediation object B, B to access a to achieve the purpose, this way we are called proxies.
Here object A belongs to the class that we call the delegate class, also known as the proxy class, and object B belongs to the class called the proxy class.
The advantages of the agent are:

    • To hide the implementation of a delegate class
    • Decoupling, do some extra processing without changing the delegate class code, such as adding initial judgment and other public operations

Depending on whether the agent class already exists before the program runs, you can divide the agent into static agents and dynamic proxies.

1.2 Static proxies

Proxy classes that exist before a program is run are called static proxies.
As you can see from the above explanation, the way that the developer writes or the compiler generates the proxy class belongs to the static proxy, and the following is a simple static proxy instance:

class ClassA {    public void operateMethod1() {};    public void operateMethod2() {};    public void operateMethod3() {};}public class ClassB {    private ClassA a;    public ClassB(ClassA a) {        this.a = a;    }    public void operateMethod1() {        a.operateMethod1();    };    public void operateMethod2() {        a.operateMethod2();    };    // not export operateMethod3()}

The above ClassA is the delegate class, ClassB is the proxy class, ClassB the function is directly called the ClassA corresponding function, and Class hidden operateMethod3() functions.

Proxy classes and delegate classes in static proxies also often inherit the same parent class or implement the same interface.

1.3 Dynamic Agents

The proxy class does not exist before the program runs, and the proxy mode that is dynamically generated by the program is called a dynamic proxy.

Java provides a way for dynamic proxies to dynamically generate proxy classes at run time. One of the great benefits of this proxy approach is that it makes it easy to do uniform or special handling of the functions of the proxy class, such as recording all function execution times, adding validation judgments before all functions are executed, and special operations on a particular function rather than modifying each function as a static proxy.

静态代理Relatively simple, this article has been briefly introduced, the following emphasis is introduced 动态代理 .

2. Dynamic proxy instance Implementation Dynamic Agent includes three steps:

(1). New delegate class;
(2). Implement InvocationHandler interfaces, which are the interfaces that the intermediate classes that are responsible for connecting proxy classes and delegate classes must implement;
(3). Proxy Create a new proxy class object from the class.

The following is a concrete introduction to the example, if now we want to count the execution time of all functions of a class, the traditional way is in front of each function of the class statistics, dynamic proxy mode is as follows:

2.1 New Delegate Class
 public interface Operate {public void operateMethod1 ();    public void operateMethod2 (); public void OperateMethod3 ();} public class Operateimpl implements Operate {@Override public void operateMethod1 () {System.out.println (' in        Voke operateMethod1 ");    Sleep (110);        } @Override public void OperateMethod2 () {System.out.println ("Invoke operateMethod2");    Sleep (120);        } @Override public void OperateMethod3 () {System.out.println ("Invoke operateMethod3");    Sleep (130);        } private static void sleep (long millseconds) {try {thread.sleep (millseconds);        } catch (Interruptedexception e) {e.printstacktrace (); }    }}

Operateis an interface that has certain functions, and we want to count the execution time of these functions.
OperateImplis the delegate class that implements the Operate interface. Each function simply outputs a string and waits for some time.
The dynamic proxy requires that the delegate class implement an interface, for example, where the delegate class is OperateImpl implemented Operate , and the reason is later published on Weibo.

2.2. Implement the Invocationhandler interface
public class TimingInvocationHandler implements InvocationHandler {    private Object target;    public TimingInvocationHandler() {}    public TimingInvocationHandler(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        long start = System.currentTimeMillis();        Object obj = method.invoke(target, args);        System.out.println(method.getName() + " cost time is:" + (System.currentTimeMillis() - start));        return obj;    }}

targetproperty represents a delegate class object.

InvocationHandleris the interface that the intermediate classes that are responsible for connecting the proxy class and the delegate class must implement. Only one of them

public Object invoke(Object proxy, Method method, Object[] args)

Functions need to be implemented, parameters:
proxyRepresents the following 2.3 通过 Proxy.newProxyInstance() 生成的代理类对象 .
methodRepresents the function that the proxy object is called.
argsRepresents the parameters of a function called by a proxy object.

Each function that invokes the proxy object actually ultimately is the function that was called. InvocationHandler invoke Here we invoke add the start-end timing in the implementation, which also invokes the corresponding function of the delegate class object target , thus completing the statistical execution time requirement.
invokefunction, we can also method make some judgments, so that some of the functions of special processing.

2.3. Generating proxy objects through proxy class static functions
 public class Main {public static void Main (string[] args) {//Create proxy Instan        Ce timinginvocationhandler timinginvocationhandler = new Timinginvocationhandler (new Operateimpl ());                Operate Operate = (Operate) (Proxy.newproxyinstance (Operate.class.getClassLoader (), new class[] {Operate.class},        Timinginvocationhandler));        Call method of Proxy instance operate.operatemethod1 ();        System.out.println ();        OPERATE.OPERATEMETHOD2 ();        System.out.println ();    Operate.operatemethod3 (); }}

Here we first new OperateImpl() create the object as a TimingInvocationHandler constructor in the delegate class object timingInvocationHandler ;
Then Proxy.newProxyInstance(…) a proxy object is created by the function, and the actual proxy class is generated dynamically at this time. The function that we call the proxy object is called timingInvocationHandler invoke (not a bit like a static proxy), whereas the function invoke implementation calls the corresponding method of the delegate class object new OperateImpl() (not a bit like a static proxy).

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

loaderRepresents the class loader
interfacesRepresents the interface of the delegate class, which is required to implement when generating the proxy class
his the InvocationHandler implementation class object that is responsible for connecting the intermediate classes of the proxy class and the delegate class

We can understand that, as on the dynamic proxy implementation is actually a two-tier static proxy, the developer provides the delegate class B, the program dynamically generated proxy class A. The developer also needs to provide an implemented InvocationHandler subclass C, a subclass C connection proxy Class A and a delegate class B, which is the delegate class of the proxy class A, delegate Class B proxy class. The user calls the object of the proxy class A directly, a forwards the call to the delegate Class C, and the delegate Class C forwards the call to its delegate class B.

3. Dynamic Agent Principle

In fact, the last paragraph has already cleared up the real principle of dynamic agent. Let's analyze it carefully.

3.1 Generated dynamic proxy class code

Here is the dynamic proxy class code generated automatically when the sample program is run, and how to get the generated code see Proxyutils, view class file can be used Jd-gui

Import Com.codekk.java.test.dynamicproxy.operate;import Java.lang.reflect.invocationhandler;import Java.lang.reflect.method;import Java.lang.reflect.proxy;import java.lang.reflect.UndeclaredThrowableException;  Public final class $Proxy 0 extends Proxy implements operate{private static Method M4;  private static Method M1;  private static Method M5;  private static Method M0;  private static Method m3;  private static Method m2;  Public $Proxy 0 (Invocationhandler Paraminvocationhandler) throws {super (Paraminvocationhandler);      Public final void OperateMethod1 () throws {try {H.invoke (this, M4, NULL);    Return } catch (error|    RuntimeException localerror) {throw localerror;    } catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable); }} public Final Boolean equals (Object Paramobject) throws {try {return (Boolean) H.invoke (this, M1, n EW object[] {paramobject}). booleanvalue(); } catch (error|    RuntimeException localerror) {throw localerror;    } catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);      }} public final void OperateMethod2 () throws {try {H.invoke (this, M5, NULL);    Return } catch (error|    RuntimeException localerror) {throw localerror;    } catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);    }} public final int hashcode () throws {try {return ((Integer) H.invoke (this, M0, null)). Intvalue (); } catch (error|    RuntimeException localerror) {throw localerror;    } catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);      }} public final void OperateMethod3 () throws {try {H.invoke (this, M3, null);    Return } catch (error|    RuntimeException localerror) {throw localerror; } CATCH (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);    }} public final String toString () throws {try {return (String) H.invoke (this, M2, null); } catch (error|    RuntimeException localerror) {throw localerror;    } catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable); }} static {try {m4 = Class.forName ("Com.codekk.java.test.dynamicproxy.Operate"). GetMethod ("OperateMethod1      ", new Class[0]);      M1 = Class.forName ("Java.lang.Object"). GetMethod ("equals", new class[] {class.forname ("Java.lang.Object")});      M5 = Class.forName ("Com.codekk.java.test.dynamicproxy.Operate"). GetMethod ("OperateMethod2", new Class[0]);      M0 = Class.forName ("Java.lang.Object"). GetMethod ("Hashcode", new Class[0]);      M3 = Class.forName ("Com.codekk.java.test.dynamicproxy.Operate"). GetMethod ("OperateMethod3", new Class[0]); M2 = Class.forName ("Java.lang.Object").GetMethod ("ToString", new Class[0]);    Return } catch (Nosuchmethodexception localnosuchmethodexception) {throw new Nosuchmethoderror (LOCALNOSUCHMETHODEXCEP    Tion.getmessage ()); } catch (ClassNotFoundException localclassnotfoundexception) {throw new Noclassdeffounderror (Localclassnotfoun    Dexception.getmessage ()); }  }}

From this we can see that the dynamically generated proxy class is a $Proxy class that inherits from the prefix of the class name and Proxy implements all the Proxy.newProxyInstance(…) interfaces passed in by the second parameter.
If a non-public interface exists in the interface implemented by the proxy class, its package name is the package name of the interface, otherwise com.sun.proxy .
In which, the operateMethod1() operateMethod2() operateMethod3() function is given directly h to the processing, h defined in the parent class Proxy as

protected InvocationHandler h;

Is the Proxy.newProxyInstance(…) third parameter.
So InvocationHandler the subclass C joins the proxy class A and delegate Class B, which is the delegate class of proxy Class A, which delegates Class B's proxy class.

3.2. How to generate dynamic proxy classes

The following is for Java 1.6 source code analysis, dynamic proxy classes are Proxy.newProxyInstance(…) generated when the function is called.

(1). Newproxyinstance (...)

The function code is as follows:

public static Object newproxyinstance (ClassLoader loader, Cla Ss<?>[] interfaces, Invocationhandler h) throws illegalargumentexception{ if (h = = null) {throw new NullPointerException (); }/* * Look up or generate the designated proxy class. */Class CL = getproxyclass (loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */try {Constructor cons = Cl.getconstructor (constructorparams); Return (Object) cons.newinstance (new object[] {h}); } catch (Nosuchmethodexception e) {throw new Internalerror (e.tostring ()); } catch (Illegalaccessexception e) {throw new Internalerror (e.tostring ()); } catch (Instantiationexception e) {throw new Internalerror (e.tostring ()); } catch (InvocationTargetException e) {throw new Internalerror (e.tostring ()); }}

It can be seen that it calls the getProxyClass(loader, interfaces) dynamic proxy class first, and then InvocationHandler acts as a proxy class constructor into the new proxy class object.

(2). Getproxyclass (...)

The function code and explanation are as follows (omit the original English comment):

/** * Gets the proxy class, does not exist dynamically generated * @param loader proxy class belongs to ClassLoader * @param interfaces proxy class needs to implement Interface * @return */public static class< > Getproxyclass (ClassLoader loader, class<?> .... interfaces) throws illegal argumentexception{if (Interfaces.length > 65535) {throw new IllegalArgumentException ("Interface limit Excee    Ded ");    }//proxy class object class proxyclass = null; /* Collect interface names to use as key for proxy class cache */string[] Interfacenames = new String[interfaces.lengt    h];       Set interfaceset = new HashSet ();     For detecting Duplicates/** * Interfaces test, contains three parts * (1) Whether in the ClassLoader specified in the entry parameter * (2) is Interface * (3) whether there are duplicates in interfaces */for (int i = 0; i < interfaces.length; i++) {String InterfaceName = interface        S[i].getname ();        Class interfaceclass = null;        try {interfaceclass = Class.forName (InterfaceName, false, loader); } catch (ClassnotfouNdexception e) {} if (Interfaceclass! = Interfaces[i]) {throw new IllegalArgumentException (        Interfaces[i] + "is no visible from class loader"); } if (!interfaceclass.isinterface ()) {throw new IllegalArgumentException (Interfaceclass        . GetName () + "isn't an interface"); } if (Interfaceset.contains (Interfaceclass)) {throw new IllegalArgumentException ("Repea        Ted Interface: "+ interfaceclass.getname ());        } interfaceset.add (Interfaceclass);    Interfacenames[i] = InterfaceName;    }//The List that corresponds to the interface name as the cached key Object key = Arrays.aslist (Interfacenames);    /* * Loadertocache is a two-tier MAP * The first layer key is ClassLoader, the second key is the weak reference of the List,value as the proxy class */map cache;        Synchronized (loadertocache) {cache = (MAP) loadertocache.get (loader);            if (cache = = null) {cache = new HashMap (); Loadertocache.put (LoadeR, Cache); }/* * To find the proxy class with the List of the interface name above, if the result is: * (1) Weak reference, indicating that the proxy class is already in the cache * (2) Pendinggenerationmarker object, representing the proxy class     In the build, wait for the build to complete the notification. * (3) NULL means not in cache and does not start build, add tag to cache, continue to generate proxy class */synchronized (cache) {do {Object value = cache.ge            T (key);            if (value instanceof Reference) {Proxyclass = (Class) ((Reference) value). get (); } if (Proxyclass! = null) {//proxy class already Generated:return it return PR            Oxyclass; } else if (value = = Pendinggenerationmarker) {//proxy class being generated:wait for it T                ry {cache.wait ();            } catch (Interruptedexception e) {} continue;                } else {cache.put (key, Pendinggenerationmarker);            Break    }} while (true);     } try {String proxypkg = null; Package to Define proxy class in/* * If there are non-public interfaces in the interfaces, all non-public interfaces must be under the same package, and the subsequent generated proxy classes will also be under the package */            for (int i = 0; i < interfaces.length; i++) {int flags = Interfaces[i].getmodifiers (); if (!                Modifier.ispublic (Flags)) {String name = Interfaces[i].getname ();                int n = name.lastindexof ('. '); String pkg = ((n = =-1)?                "": name.substring (0, n + 1));                if (proxypkg = = null) {proxypkg = pkg; } else if (!pkg.equals (proxypkg)) {throw new IllegalArgumentException ("Non-pub                LIC interfaces from different packages ");           }}} if (proxypkg = = null) {//If no Non-public proxy interfaces, proxypkg = "";            Use the unnamed package} {//Gets the class name of the proxy class, which is missing from the JDK 1.6 version of the processing that already exists for this generated class.            Long num; Synchronized (nextuniquEnumberlock) {num = nextuniquenumber++;            } String proxyname = proxypkg + proxyclassnameprefix + num; Dynamically generated proxy class byte code//Final Call Sun.misc.ProxyGenerator.generateClassFile () Get proxy class related information written to DataOutputStream implementation B            yte[] Proxyclassfile = Proxygenerator.generateproxyclass (proxyname, interfaces);                    The try {//Native layer implementation, the virtual machine loads the proxy class and returns its class object proxyclass = DefineClass0 (loader, Proxyname,            Proxyclassfile, 0, proxyclassfile.length);            } catch (Classformaterror e) {throw new IllegalArgumentException (e.tostring ()); }}//Add to set of all generated proxies classes, for Isproxyclass proxyclasses.put (proxyclass, NULL)    ;                The finally {///proxy class is successfully saved to the cache, otherwise it is removed from the cache and then notifies the waiting call synchronized (cache) {if (Proxyclass! = null) {            Cache.put (Key, New WeakReference (Proxyclass)); } else {                Cache.remove (key);        } cache.notifyall (); }} return proxyclass;}

The function mainly consists of three parts:

    • Interfaces test, including whether there are duplicates within the ClassLoader specified in the entry parameter, whether it is Interface, interfaces
    • The List for the interface name is the key to find the proxy class, if the result is:
      • A weak reference that indicates that the proxy class is already in the cache;
      • A Pendinggenerationmarker object that indicates that the proxy class is in the build, waiting for the build to finish returning;
      • Null means not in the cache and does not start the build, add tags to the cache, and continue to generate the proxy class.
    • If the proxy class does not exist, call the ProxyGenerator.generateProxyClass(…) generate proxy class and cache it, notifying the cache of the wait.

Some of the points of note in the function:

    • The proxy class's cache key is the interface name corresponding to the List, the interface order of different means different key is a different proxy class.
    • If there is a non-public interface in interfaces, all non-public interfaces must be under the same package, and subsequent generated proxy classes will also be under the package.
    • The proxy class is not processed if it already exists in the ClassLoader.
    • You can turn on the System Properties sun.misc.ProxyGenerator.saveGeneratedFiles switch to save the dynamic class to the destination address.

Java 1.7 Implementation is slightly different, through the getProxyClass0(…) function implementation, the implementation of the invocation of the proxy class cache, to determine whether the proxy class in the cache already exists, there is a direct return, there is no call proxyClassCache to the properties of the valueFactory dynamic generation, valueFactory the apply function getProxyClass(…) of the above function logic is similar.

4. Use Scenario 4.1 The AOP (aspect-oriented programming) feature of Spring in Java EE Web development

Function: decoupling between target functions.
In Dao, for example, every database operation needs to open a transaction, and you need to pay attention to the permissions when you operate. The general notation is to add logic to each of the Dao's functions, resulting in code redundancy and a high degree of coupling.
The pre-pseudo code for using dynamic proxies is as follows:

Dao {    insert() {        判断是否有保存的权限;        开启事务;        插入;        提交事务;    }    delete() {        判断是否有删除的权限;        开启事务;        删除;        提交事务;    }}

The pseudo-code using dynamic proxies is as follows:

// 使用动态代理,组合每个切面的函数,而每个切面只需要关注自己的逻辑就行,达到减少代码,松耦合的效果invoke(Object proxy, Method method, Object[] args)                    throws Throwable {    判断是否有权限;    开启事务;    Object ob = method.invoke(dao, args);    提交事务;    return ob; }
4.2 REST-based Android network request Framework Retrofit

Function: Simplifies network request operations.
In general, each network request we need to call once HttpURLConnection or HttpClient make a request, or like volley into the waiting queue, Retrofit greatly simplifies these operations, the sample code is as follows:

public interface GitHubService {  @GET("/users/{user}/repos")  List<Repo> listRepos(@Path("user") String user);}RestAdapter restAdapter = new RestAdapter.Builder()    .setEndpoint("https://api.github.com")    .build();GitHubService service = restAdapter.create(GitHubService.class);

In the future we just need to call directly

List<Repo> repos = service.listRepos("octocat");

Can start the network request, Retrofit the principle is based on dynamic agent, it also uses the principle of annotations, this article does not do in-depth introduction, specific Please wait for Retrofit source parsing completed.

"Reprint" Java dynamic agent

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.