Proxy proxy mode in detailed design pattern and implementation in Java program _java

Source: Internet
Author: User
Tags getmessage http request instance method reflection static class throwable ticket

One, the proxy mode definition

Provides a proxy object to an object and Controls access to the original object by the proxy object, that is, the client does not manipulate the original object directly, but indirectly manipulates the original object through the proxy object.
An example of a well-known proxy pattern is the reference count (reference counting): When multiple copies of a complex object are needed, the agent mode can combine the meta mode to reduce the amount of memory. A typical approach is to create a complex object and multiple agents, and each agent will refer to the original object. And the action in the agent's operation will be transferred to the original object. Once all agents do not exist, complex objects are removed.

To understand the agent model is very simple, in fact, there is a proxy mode in life:
We can buy train tickets to the railway station. But can also go to the train ticket sales office to buy, the train ticket at this place is the railway station ticket agent, that is, we issued a ticket at the sale point request, the sale point will send the request to the railway station, the railway station to buy a successful response to the consignment point, to tell you.
But the sale point can only buy tickets, can not refund, and the railway station can buy tickets can refund, so the agent object support operation may be different from the operation of the delegate object.

One more example of writing a program:
If you now have an existing project (you do not have source code, you can only call it) to be able to call the int compute (String EXP1) to implement the calculation of the suffix expression, you want to use this project to implement the calculation of infix expressions, then you can write a proxy class, And it also defines a compute (String exp2), the Exp2 argument is an infix expression, so you need to convert the infix expression to the suffix expression (preprocess) before calling the compute () of the existing project, and then invoke the compute of the existing item. (), of course, you can also receive the return value and then do some other operations such as file (PostProcess), the process is the use of proxy mode.

In peacetime with the computer will also encounter the application of Agent mode:
Remote Agent: We are not able to access Facebook because of the GFW, we can access it using the flip-wall (set proxy) method. The access process is:
(1) The user sends the HTTP request to the agent
(2) proxy sends HTTP request to Web server
(3) The Web server sends the HTTP response to the agent
(4) The agent sends the HTTP response back to the user


second, static agent

The so-called static agent, that is, in the compile phase of the generation of proxy classes to complete a series of operations on the proxy object. The following is the structure class diagram of the proxy pattern:

1, Agent mode of participants

The role of Agent mode is divided into four types:

Subject interface: The behavior interface implemented by the proxy class.
Target object: Is the object that is being represented.
Proxy object: A proxy class used to encapsulate a really subject class
Client
The following is the class diagram structure of the proxy pattern:

2, the implementation of the agent mode of thinking

Both the proxy object and the target object implement the same behavior interface.
The proxy class and the target class implement the interface logic respectively.
Instantiates a target object in the constructor of the proxy class.
Invokes the behavior interface of the target object in the proxy class.
The client wants to invoke the behavior interface of the target object, which can only be done through the proxy class.
3, the static proxy instance

The following is an example of a deferred load to illustrate the static proxy. When we start a service system, it can take a long time to load a class. In order to get better performance, when starting the system, we often do not initialize the complex class, instead of initializing its proxy class. In this way, the resource-intensive method is used to separate the agent, which can speed up the start-up of the system and reduce the time that the user waits.

Define a Theme interface

Public interface Subject {public
  void SayHello ();
  public void Saygoodbye ();
}

Define a target class and implement the theme interface

public class Realsubject implements Subject {public
  void SayHello () {
    System.out.println ("Hello World");
  } Public
  void Saygoodbye () {
    System.out.println ("GoodBye World");
  }

Define a proxy class to proxy the target object.

public class Staticproxy implements Subject {
  Private realsubject realsubject = null;
  Public Staticproxy () {}
  is public void SayHello () {
    //is loaded at the time, lazy load
    if (Realsubject = null) {
      Realsubject = new Realsubject ();
    }
    Realsubject.sayhello ();
  }
  Saygoodbye method ...
}

Define a client

public class Client {public
  static void Main (String [] args) {
    Staticproxy sp = new Staticproxy ();
    Sp.sayhello ();
    Sp.saygoodbye ();
  }

The above is a simple test example of a static proxy. The senses may have no practical use. But that is not the case. Using proxies we can also transform the methods of the target object, such as creating a series of connections in the database connection pool, which are rarely closed to allow for infrequent open connections. However, we always have the habit of programming the open connection to close. In this way, we can use proxy mode to connection the Close method in the interface, and change to recycle to the database connection pool instead of the actual execution Connection#close method. There are many other examples, the specific needs of their own experience.

Third, dynamic agent

Dynamic proxies are dynamically generated proxy classes at run time. That is, the bytecode for the proxy class will be generated at run time and loaded into the current agent's ClassLoader. Dynamic classes have many benefits compared to static processing classes.

There is no need to write a formal package for the real topic, if there are many methods in the topic interface, it is also troublesome to write an agent method for each interface. If the interface is changed, the real subject and proxy class will be modified, which is not conducive to system maintenance;
Using some dynamic proxy generation methods can even make the execution logic of the proxy class at runtime, thus greatly improving the flexibility of the system.
There are many ways to generate dynamic proxies: JDK with dynamic proxies, cglib, javassist, etc. Each of these methods has advantages and disadvantages. This article mainly explores the use of dynamic agents in JDK and source analysis.

Here's an example of how dynamic proxies are used in JDK:

public class Dynamicproxy implements Invocationhandler {
  private realsubject = null;
  public object invoke (object proxy, Method method, object[] args {
    if (Realsubject = = null) {
      realsubject = new Real Subject ();
    }
    Method.invoke (Realsubject, args);
    Return Realsubject
  }
}

Client code Instances

public class Client {public
  static void Main (strings[] args) {
    Subject Subject = (Subject) proxy.newinstance (Cl Assloader.getsystemloader (), RealSubject.class.getInterfaces (), New Dynamicproxy ());
    Subject.sayhello ();
    Subject.saygoodbye ();
  }

You can see from the above code that you want to take advantage of the dynamic proxies in the JDK. Using static method Proxy.newinstance (ClassLoader, interfaces[], Invokehandler) You can create a dynamic proxy class. The Newinstance method has three parameters, representing the class loader, a list of interfaces that you want the proxy class to implement, and an instance of implementing the Invokehandler interface. The dynamic proxy gives the execution of each method to the Invoke method to process.

JDK Dynamic proxy requirements, the agent must be an interface, simple class is not. The proxy class generated by the JDK dynamic proxy inherits the proxy class, and the proxy class implements the list of interfaces that you pass in. You can therefore force types to be converted to interface types. The following is a structure diagram of the proxy.

You can see that the proxy is all static, so if the proxy class does not implement any interfaces, then he is the proxy type and there is no instance method.

Of course join you if you want to proxy a class that does not implement an interface, and the method of that class is the same as the method defined by other interfaces, it can be easily implemented using reflection.

public class Dynamicproxy implements Invokehandler {
  //You want to proxy class
  private targetclass Targetclass = null;
  Initializes the class public
  dynamicproxy (Targetclass targetclass) {
    this.targetclass = Targetclass;
  }
  public object invoke (object proxy, methods method, object[] args) {
    //Use reflection to get the way of the class you want to broker
    MyMethod = Targetclas S.getclass (). Getdeclaredmethod (Method.getname (), method.getparametertypes ());
    Mymethod.setaccessible (true);
    Return Mymethod.invoke (Targetclass, args);
  }

Iv. JDK Dynamic Agent Source code analysis (JDK7)

Looking at the example above, we simply use a dynamic proxy. However, it is still foggy how the proxy class is created and who invokes the Invoke method. Below by analyzing

1, how is the proxy object created?

First look at the source code of the Proxy.newinstance method:

public static Object newproxyinstance (ClassLoader loader, class<?>[] interfaces, Invocationhandler h) throws illegalargumentexception {
  }
  //Get interface Information
  final class<?>[] Intfs = Interfaces.clone ();
  Final SecurityManager sm = System.getsecuritymanager ();
  if (SM!= null) {
    checkproxyaccess (Reflection.getcallerclass (), loader, intfs);
  Generate proxy class
  class<?> cl = getProxyClass0 (loader, intfs);
  // ... OK, let's see the front half first.
  

From the source to see proxy class generation is relying on getProxyClass0 this method, then look GETPROXYCLASS0 Source:

private static class<?> GetProxyClass0 (ClassLoader loader, class<?> ... interfaces) {
  // The number of interface lists cannot exceed 0xFFFF
  if (Interfaces.length > 65535) {
    throw new IllegalArgumentException ("interface limit Exceeded ");
  }
  Note here that the return 
    proxyclasscache.get (loader, interfaces) is explained in detail below;
  

The explanation for Proxyclasscache.get is: If the proxy class that implements the interface list already exists, take it directly from the cache. If it does not exist, one is generated by proxyclassfactory.
Before looking at Proxyclasscache.get source code, first a brief understanding Proxyclasscache:

 Private static final Weakcache<classloader, class<?>[], class<?>>
    proxyclasscache = new Weakcache<> (New Keyfactory (), New Proxyclassfactory ());

Proxyclasscache is a weakcache type of cache whose constructors have two parameters, one of which is the proxyclassfactory used to generate the proxy class, and the following is the Proxyclasscache.get source code:

Final class weakcache<k, P, v> {
  ...
  Public V get (K key, P parameter) {}
}

Here K represents key, p is parameters, V represents value

The public V get (K key, P parameter) {//java7 Nullobject the method of judgment and throws an exception with the specified message if parameter is empty.
  Returns if it is not empty.
  Objects.requirenonnull (parameter);
  Clean up the weakhashmap that hold weak references, generally used for caching expungestaleentries ();
  Gets the CacheKey Object CacheKey = cachekey.valueof (key, Refqueue) from the queue; Using lazy loading to populate Supplier, concurrent is a thread-safe map concurrentmap<object, supplier<v>> valuesmap = Map.get (
  CacheKey); if (Valuesmap = = null) {concurrentmap<object, supplier<v>> oldvaluesmap = map.putifabsent (CacheKey, value
      SMap = new concurrenthashmap<> ());
      if (Oldvaluesmap!= null) {valuesmap = Oldvaluesmap; }//Create subkey and retrieve the possible supplier<v> stored by so//subkey from Valuesmap OBJ
  ECT subkey = Objects.requirenonnull (Subkeyfactory.apply (key, parameter));
  supplier<v> Supplier = Valuesmap.get (subkey);
  Factory Factory = null; while (true) {if (supplier!= null) {//Get value from supplier, this value may be a factory or CACHe's real//The following three code is the core code, return to implement Invokehandler class and contains the required information.
      V value = Supplier.get ();
      if (value!= null) {return value; }//Else no supplier in cache//or a supplier this returned null (could be a cleared cachevalue//or
      A Factory that wasn ' t successful in installing the Cachevalue)//The following procedure is the process of populating the supplier if (Factory = null) {
 Create a factory} if (supplier = = NULL) {//Fill supplier}else {//Fill supplier}}

The

While loop is a constant acquisition of classes that implement Invokehandler, which can be obtained from the cache, but generated from Proxyfactoryclass. The
factory is an internal class that implements the Supplier<v> interface. This class overrides the Get method and calls the instance method of type Proxyfactoryclass in the Get method apply. This method is really the way to create a proxy class. Below look at the source code of the Proxyfactoryclass#apply method:

Public class<?> Apply (ClassLoader loader, class<?>[] interfaces) {map<class<?>, boolean> Inter
  Faceset = new identityhashmap<> (interfaces.length); for (class<?> intf:interfaces) {/* Verify that's Class loader resolves the name of this interface to the SA
    Me Class object.*/class<?> interfaceclass = null;
    try {//load information for each interface runtime Interfaceclass = Class.forName (Intf.getname (), false, loader); 
    The catch (ClassNotFoundException e) {}//If the class that you loaded with your own classload is not equal to the class you passed in, throw an exception if (Interfaceclass!= intf) {
  throw new IllegalArgumentException (intf + "is not visible from class loader"); //If incoming is not an interface type if (!interfaceclass.isinterface ()) {throw new IllegalArgumentException (INTERFACECLA
    Ss.getname () + "is isn't an interface"); }//Verify that the interface repeats if (Interfaceset.put (Interfaceclass, boolean.true)!= null) {throw new illegalargumentexception ("Repeated interface:" + inteRfaceclass.getname ());   } String proxypkg = null; Package to define proxy class in/* record the package of a Non-public proxy interface so this proxy class would b 
  e defined in the same package.
  * Verify that all Non-public proxy interfaces are the same package. *//This section is to see if you have incoming interfaces that are not public interfaces, and if so, these interfaces must all be defined in a package, otherwise thrown exception for (class<?> intf:interfaces) {int flags = i
    Ntf.getmodifiers (); if (!
      Modifier.ispublic (Flags)) {String name = Intf.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-public Interfaces from diff
      Erent packages "); }} if (proxypkg = null) {//If no Non-public proxy interfaces, use Com.sun.proxy package proxypkg = R
  Eflectutil.proxy_package + "."; }/* Choose a name for the proxy class to generate.
  */Long num = Nextuniquenumber.getandincrement ();
  Generates the class name of the random proxy class, $Proxy + num String proxyname = proxypkg + proxyclassnameprefix + num;
  * * Generate proxy class file, return byte stream * * byte[] proxyclassfile = Proxygenerator.generateproxyclass (proxyname, interfaces);
  try {return DefineClass0 (loader, proxyname, proxyclassfile, 0, proxyclassfile.length);
      catch (Classformaterror e) {//End throw new IllegalArgumentException (e.tostring ());
 }
    }
  }

It is not accurate to mention that proxyfactoryclass#apply is really a way to generate proxy classes. Source code read here, we will find that Proxygenerator#generateproxyclass is really the way to generate proxy classes. Based on the Java class bytecode (see my other Article Java bytecode Learning notes) to generate the corresponding CLSS file. Specific Proxygenerator#generateproxyclass source code is as follows:

  Private byte[] Generateclassfile () {/* Step 1:assemble Proxymethod objects for all methods to * gener
     Ate proxy dispatching code for. * *//addproxymethod method is to add the method to a list, and corresponding to the corresponding class//here corresponds to object three methods hashcode,tostring and equals Addproxymeth
    OD (Hashcodemethod, Object.class);
    Addproxymethod (Equalsmethod, Object.class);
    Addproxymethod (Tostringmethod, Object.class); Corresponds the interface in the interface list to the method under the interface for (int i = 0; i < interfaces.length i++) {method[] methods = Interfaces[i].getmeth
      ODS ();
      for (int j = 0; J < Methods.length J + +) {Addproxymethod (methods[j], interfaces[i]); }/* For each set of proxy methods with the same signature, * Verify this methods ' return types
     are compatible.
    * For (list<proxymethod> sigmethods:proxyMethods.values ()) {checkreturntypes (sigmethods); } * * Step 2:assemble FieldInfo and MethodInfo structs for all of * FielDS and methods in the class we are generating. * *//method to add the construction method, this construction method is only one, is a construction method with Invocationhandler interface//This is really to the class file, that is, the proxy class to join the method, but has not really dealt with, but first add in wait for the loop, the construction method in
    The name description in the class file is <init> try {methods.add (Generateconstructor ()); For (list<proxymethod> sigmethods:proxyMethods.values ()) {for (Proxymethod pm:sigmethods) {//each proxy method plus a The attribute of the method type, the number 10 is the identifier of the class file that represents the private static Fields.Add (new FieldInfo pm.methodfieldname, "Lja Va/lang/reflect/method; ", Acc_private |
        acc_static));
      Add each proxy method to the proxy class method Methods.add (Pm.generatemethod ()); }//Add a static initialization block, initialize each attribute, where the static code block is also called the class construction method, in fact, is the name <clinit> method, so add to the method list Methods.add (Generatestaticinitia
    Lizer ());
    catch (IOException e) {throw new Internalerror ("Unexpected I/O Exception"); The number of methods and properties cannot exceed 65535, this is also true of the number of previous interfaces,//This is because in the class file, these numbers are represented in 4-bit 16, so the maximum value is 2 of 16 times-1 if (Methods.size () > 655
      35) {throw new IllegalArgumentException ("Method limit Exceeded");
    } if (Fields.size () > 65535) {throw new IllegalArgumentException ("Field limit exceeded"); //Next is the process of writing a class file, including the magic number, class name, constant pool, and so on a series of bytecode composition, not one to elaborate.
    The knowledge needed to reference the JVM virtual machine bytecode.
    Cp.getclass (Dottoslash (className));
    Cp.getclass (Superclassname);
    for (int i = 0; i < interfaces.length i++) {Cp.getclass (Dottoslash (Interfaces[i].getname ()));
    } cp.setreadonly ();
    Bytearrayoutputstream bout = new Bytearrayoutputstream ();
    DataOutputStream dout = new DataOutputStream (bout);
      try {//U4 magic;
                    Dout.writeint (0xCAFEBABE);
      U2 minor_version;
                    Dout.writeshort (classfile_minor_version);
      U2 major_version;
      Dout.writeshort (classfile_major_version);       Cp.write (dout);
      (write constant pool)//U2 access_flags; Dout.writeshort (Acc_public | acc_final |
               Acc_super);     U2 This_class;
                    Dout.writeshort (Cp.getclass (Dottoslash (ClassName)));
      U2 Super_class;
                    Dout.writeshort (Cp.getclass (superclassname));
      U2 Interfaces_count;
                    Dout.writeshort (interfaces.length);
      U2 Interfaces[interfaces_count]; for (int i = 0; i < interfaces.length i++) {dout.writeshort (Cp.getclass dottoslash (interfaces[i].g
      Etname ()));
      }//U2 fields_count;
                    Dout.writeshort (Fields.size ());
      Field_info Fields[fields_count];
      for (FieldInfo f:fields) {f.write (dout);
      }//U2 methods_count;
                    Dout.writeshort (Methods.size ());
      Method_info Methods[methods_count];
      for (MethodInfo m:methods) {m.write (dout);
      }//U2 attributes_count; Dout.writeshort (0); (No Classfile attributes for proxy classes)} catch (IOException e)
    {throw new Internalerror ("Unexpected I/O Exception");
  return Bout.tobytearray ();
 }

After layers of calls, a proxy class is finally generated.

2, who invoked the Invoke?

We simulate the JDK to generate a proxy class, the class name is Testproxygen:

public class Testgeneratorproxy {public
  static void Main (string[] args) throws IOException {
    byte[] classfile = P Roxygenerator.generateproxyclass ("Testproxygen", Subject.class.getInterfaces ());
    File File = new file ("/users/yadoao/desktop/testproxygen.class");
    FileOutputStream fos = new FileOutputStream (file); 
    Fos.write (classfile); 
    Fos.flush (); 
    Fos.close (); 
  }

Using Jd-gui to decompile the class file, the result is as follows:

Import Com.su.dynamicProxy.ISubject;
Import Java.lang.reflect.InvocationHandler;
Import Java.lang.reflect.Method;
Import Java.lang.reflect.Proxy;
Import java.lang.reflect.UndeclaredThrowableException;
 Public final class Testproxygen extends Proxy implements Isubject {private static method M3;
 private static method M1;
 private static method M0;
 private static method M4;
 private static method m2;
 Public Testproxygen (Invocationhandler Paraminvocationhandler) throws {super (Paraminvocationhandler);
   Public final void SayHello () throws {try {This.h.invoke (this, M3, 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) This.h.invoke (this, M1, n
  EW object[] {paramobject}). Booleanvalue (); catch (error| RuntimeException LocAlerror) {throw localerror;
  catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);
  } public final int hashcode () throws {try {return ((Integer) This.h.invoke (this, M0, null)). Intvalue (); catch (error|
  RuntimeException localerror) {throw localerror;
  catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);
   } public final void Saygoodbye () throws {try {This.h.invoke (this, M4, NULL);
  Return catch (error|
  RuntimeException localerror) {throw localerror;
  catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);
  } Public final String toString () throws {try {return (String) This.h.invoke (this, M2, null); catch (error|
  RuntimeException localerror) {throw localerror; catch (Throwable localthrowable) {throw new undeclaredthrowableexception (localthrowable);
  } static {try {m3 = class.forname ("Com.su.dynamicProxy.ISubject"). GetMethod ("SayHello", new Class[0));
   M1 = Class.forName ("Java.lang.Object"). GetMethod ("equals", new class[] {class.forname ("Java.lang.Object")});
   M0 = Class.forName ("Java.lang.Object"). GetMethod ("Hashcode", new class[0);
   M4 = Class.forName ("Com.su.dynamicProxy.ISubject"). GetMethod ("Saygoodbye", new class[0);
   M2 = Class.forName ("Java.lang.Object"). GetMethod ("ToString", new Class[0]);
  Return catch (Nosuchmethodexception localnosuchmethodexception) {throw new Nosuchmethoderror (localnosuchmethodexception
  . GetMessage ()); catch (ClassNotFoundException localclassnotfoundexception) {throw new Noclassdeffounderror (Localclassnotfoundexc
  Eption.getmessage ());
 }
 }
}

The

First takes note of the constructor of the build proxy class, which passes in a class that implements the Invokehandler interface as a parameter and invokes the constructor of the parent proxy, initializing the member variable protected Invokehander h in the proxy. The
again takes note of several static initialization blocks, where the static initialization block initializes the interface list of the proxy as well as the hashcode,tostring, Equals method. The
finally is the calling procedure for these methods, all of which are callback invoke methods.
This is the end of this agent mode 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.