Java design mode-proxy dynamic proxy mode

Source: Internet
Author: User
Tags throw exception throwable unsupported

Proxy: Design mode

A proxy is a common design pattern designed to provide a proxy for other objects to control access to an object. The proxy class is responsible for preprocessing the message for the delegate class, filtering the message and forwarding the message, and subsequent processing after the message is delegated to the class.

Figure 1. Proxy mode

In order to maintain the consistency of the behavior, the proxy class and the delegate class usually implement the same interface, so there is no difference between the visitors. Through the intermediate layer of proxy class, the direct access to delegate class object can be controlled effectively, and the delegate class object can be hidden and protected well, and space is reserved for implementing different control strategies, which gives more flexibility in design. The Java dynamic agent mechanism is a clever way to practice the design concept of the proxy mode in near perfect fashion.

Related classes and interfaces

To understand the mechanics of a Java dynamic agent, you first need to understand the following related classes or interfaces:

  • Java.lang.reflect.Proxy: This is the main class of the Java dynamic agent mechanism, which provides a set of static methods for dynamically generating proxy classes and their objects for a set of interfaces. Listing 1. static method of Proxy
    Method 1: The method is used to get the specified proxy object associated with the calling processor static Invocationhandler Getinvocationhandler (object proxy)//method 2: The method is used to obtain the class object of the dynamic proxy class associated with the specified class loader and a set of interfaces static class Getproxyclass (ClassLoader loader, class[] interfaces)//method 3: This method is used to determine whether the specified class object is a dynamic proxy class static Boolean Isproxyclass (class CL)//Method 4: This method is used to generate a dynamic proxy class instance static object for the specified class loader, a set of interfaces, and the calling processor Newproxyinstance (ClassLoader loader, class[] interfaces,     Invocationhandler h)
  • Java.lang.reflect.InvocationHandler: This is the call processor interface, which customizes an invoke method that centralizes the method invocation on the dynamic proxy class object, typically implementing proxy access to the delegate class in the method. Listing 2. The core approach of Invocationhandler
    This method is responsible for centralizing all method calls on the dynamic proxy class. The first parameter is both a proxy class instance, the second parameter is the method object being called//the third method is the call parameter. The calling processor is preprocessed or dispatched to the delegate class instance based on these three parameters to execute object invoke (object proxy, method, object[] args)

    Each time a dynamic proxy class object is generated, you need to specify a call processor object that implements the interface (see the third parameter of Proxy static method 4).

  • Java.lang.ClassLoader: This is the class loader class, which is responsible for loading the bytecode of the class into the Java virtual machine (JVM) and defining the class object for it, and then the class can be used. Proxy static method generating dynamic proxy classes also need to be loaded through a class loader to use, and the only difference from the normal class is that its bytecode is generated dynamically by the JVM at runtime rather than pre-existing in any. class file.

    You need to specify a class loader object each time a dynamic proxy class object is generated (see the first parameter of the proxy static method 4)

Agent mechanism and its characteristics

First let's look at how to use the Java dynamic agent. The following four steps are specific:

    1. Create your own calling processor by implementing the Invocationhandler interface;
    2. Create a dynamic proxy class by specifying a ClassLoader object and a set of interface for the proxy class;
    3. The constructor of the dynamic proxy class is obtained through the reflection mechanism, and its unique parameter type is the calling processor interface type;
    4. Creates an instance of a dynamic proxy class through a constructor that constructs a call to a processor object as a parameter is passed in.
Listing 3. Dynamic proxy object creation process
Invocationhandlerimpl implements the Invocationhandler interface and can implement the dispatch and forwarding of method calls from the proxy class to the delegate class//its interior typically contains a reference to the delegate class instance. A method call Invocationhandler handler = new Invocationhandlerimpl (..) used to actually perform the dispatch forwarding over Class object for dynamically creating proxy classes by proxy for a set of interfaces including the Interface interface class Clazz = Proxy.getproxyclass (ClassLoader, new class[] {Interface.class , ... }); Gets the constructor object from the generated class object by reflection constructor constructor = Clazz.getconstructor (new class[] {invocationhandler.class}); Create a dynamic proxy class instance from a constructor object Interface proxy = (Interface) constructor.newinstance (new object[] {handler});

The actual use of the process is more simple, because the static method of Proxy Newproxyinstance has encapsulated steps 2 through 4 of the process, so the simplified process is as follows

Listing 4. Simplified dynamic proxy object creation process
Invocationhandlerimpl implements the Invocationhandler interface and enables the dispatch and forwarding of method calls from the proxy class to the delegate class Invocationhandler handler = new Invocationhandlerimpl (..); Create a dynamic proxy class instance directly via proxy Interface proxy = (Interface) proxy.newproxyinstance (ClassLoader,  new class[] { Interface.class},  handler);

Now let's look at some of the features of the Java dynamic agent mechanism.

First, there are some characteristics of the dynamically generated proxy class itself. 1) Package: If the interface being proxied is public, then it will be defined in the top-level package (that is, the package path is empty), if there is a non-public interface in the Proxied interface (because the interface cannot be defined as protect or private, it is the default except public) Package access level), then it will be defined in the packet where the interface is located (assuming that a non-public interface A is represented in the Com.ibm.developerworks package, the package that contains the newly generated proxy class is Com.ibm.developerworks), the purpose of this design is to ensure that the dynamic proxy class is not able to be successfully defined and accessed due to the problem of package management; 2) class modifier: The proxy class has the final and public modifiers, which means that it can be accessed by all classes , but cannot be re-inherited; 3) class name: The format is "$ProxyN", where N is an increment of Arabic numerals, representing the proxy class nth generation of dynamic proxy class, it is worth noting that not every time the static method call proxy to create a dynamic proxy class will increase the N value, The reason is that if you attempt to create a dynamic proxy class repeatedly for the same set of interfaces (including the order of the interfaces), it will be wise to return the class object of the proxy class that was previously created, instead of trying to create a completely new proxy class, which saves unnecessary code duplication and increases the efficiency of the proxy class creation. 4) class inheritance relationship: The inheritance relationship of this class

Figure 2. Inheritance diagram for dynamic proxy classes

As the diagram shows, the proxy class is its parent class, and this rule applies to all dynamic proxy classes created by proxy. And the class implements a set of interfaces that it proxies, which is why it can be safely typed to the root cause of an interface it proxies.

Let's take a look at some of the characteristics of proxy class instances. Each instance is associated with a calling processor object, which can be getinvocationhandler to get the calling processor object of the proxy class instance through the static method provided by proxy. When a method declared in the interface of its proxy is invoked on a proxy class instance, these methods will eventually be executed by the Invoke method of the calling processor, in addition, it is worth noting that there are three methods in the root class java.lang.Object of the proxy class that are also dispatched to the calling processor's invoke Method execution, which are hashcode,equals and toString, may be due to the fact that these methods are public and non-final types, can be overridden by proxy classes, and because these methods tend to present a characteristic attribute of a class with a certain degree of sensitivity, Therefore, in order to ensure that the proxy class and the delegation class external consistency, these three methods should also be assigned to the delegate class execution. When a set of interfaces for an agent has a repeating method and the method is called, the proxy class always gets the method object from the first interface and assigns it to the calling processor, regardless of whether the proxy class instance is externally referenced in the form of that interface (or a sub-interface that inherits from the interface). Because it is not possible to differentiate its current referenced type within a proxy class.

Let's look at the characteristics of a group of interfaces that are being proxied. First, be aware that you cannot have duplicate interfaces to avoid compilation errors when the dynamic proxy class code is generated. Second, these interfaces must be visible to the class loader, or the class loader will not be able to link them, causing the class definition to fail. Again, all non-public interfaces that need to be proxied must be in the same package, or the proxy class generation will fail. Finally, the number of interfaces cannot exceed 65535, which is the limit set by the JVM.

Finally, let's look at the characteristics of exception handling. From the method that invokes the processor interface declaration, it can be seen theoretically that it can throw any type of exception, because all exceptions are inherited from the Throwable interface, but is it true? The answer is no, because we must obey an inheritance principle: When a subclass overrides a parent or implements a parent interface, the exception thrown must be within the list of exceptions supported by the original method. So while the calling processor is theoretically capable, it is actually often limited unless the method in the parent interface supports throwing Throwable exceptions. So what if the Invoke method does produce an exception that is not supported in the interface method declaration? Rest assured, the Java dynamic proxy class has designed a workaround for us: it will throw a undeclaredthrowableexception exception. This exception is a runtimeexception type, so it does not cause a compilation error. With the exception's Getcause method, you can also get the original unsupported exception object for error diagnosis.

Code is the best teacher

Mechanism and features have been introduced, then let us through the source code to understand how the Proxy is implemented.

First, remember several important static variables of Proxy:

Listing 5. Important Static variables of Proxy
Mapping table: Used to maintain the class loader object to its corresponding proxy class cache private static Map Loadertocache = new Weakhashmap (); Tag: Used to mark a dynamic proxy class being created in private static object Pendinggenerationmarker = new Object (); Synchronization table: Record the type of dynamic proxy class that has been created, mainly by method Isproxyclass to determine the private static Map proxyclasses = Collections.synchronizedmap (new Weakhashmap ()); The associated call processor references protected Invocationhandler h;

Then, take a look at how the Proxy is constructed:

Listing 6. Proxy Construction Method
Since the constructor is never called directly inside a proxy, the private type means that any call to private proxy () {}//is not called directly from the constructor, so protected means that only subclasses can invoke protect Ed Proxy (Invocationhandler h) {this.h = h;}

Next, you can take a quick look at the Newproxyinstance method because it's fairly simple:

Listing 7. Proxy static Method Newproxyinstance
public static Object newproxyinstance (ClassLoader loader, class<?>[] Interfaces , Invocationhandler h) throws IllegalArgumentException {//check h is not empty, otherwise throw exception if (h = = nul     L) {throw new NullPointerException ();     }//Get the proxy class type object associated with developing a class loader and a set of interfaces class CL = Getproxyclass (loader, interfaces);         Gets the constructor object by reflection and generates a proxy class instance 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 ()); } }

Thus, the real key to dynamic proxy is the Getproxyclass method, which is responsible for dynamically generating proxy class type objects for a set of interfaces. Inside this method, you will be able to see all the heroes (static variables) in the Proxy. A little impatient, huh? Then let us walk into the most mysterious hall of Proxy to appreciate it. The method can be divided into four steps in total:

  1. A certain level of security checks on this set of interfaces, including checking that the interface class object is visible to the class loader and identical to the interface class object that the class loader recognizes, also checks to ensure that the interface type is not the class type. This step is done through a loop, which, when checked, will get an array of strings containing all the interface names, as recorded String[] interfaceNames . This part of the overall implementation is more intuitive, so omit most of the code, leaving only the relevant code to determine if a class or interface is visible to a particular class loader. Listing 8. Using the Class.forName method to judge the visibility of an interface
    try {     //Specify Interface name, Class loader object, and Initializeboolean to False for no initialization class    //If the method returns normal this indicates visible, otherwise it throws ClassNotFoundException Exception means invisible    Interfaceclass = Class.forName (InterfaceName, false, loader);} catch (ClassNotFoundException e) {}
  2. The
  3. gets the cache table that corresponds to the class loader object as a keyword from the Loadertocache mapping table, creates a new cache table if it does not exist, and updates to Loadertocache. The cache table is a HASHMAP instance, which normally holds key-value pairs (the interface name list, the class object reference of the dynamically generated proxy class). It is temporarily saved when the proxy class is being created (interface Name list, pendinggenerationmarker). The role of the tag Pendinggenerationmarke is to notify subsequent homogeneous requests (the same array of interfaces and the same order of the interfaces in the group) that the proxy class is being created, and wait until the creation is complete. Listing 9. Cache table using
    do {//With the interface name list as the keyword to get the corresponding cache value Object value = Cache.get (key);     if (value instanceof Reference) {Proxyclass = (Class) ((Reference) value). get ();     if (proxyclass! = null) {//If it has already been created, return Proxyclass directly;         } else if (value = = Pendinggenerationmarker) {//proxy class is being created, keep waiting for try {cache.wait ();     } catch (Interruptedexception e) {}//waits to be woken up, resumes looping and passes two checks to ensure that the creation is complete, or waits for continue again;         } else {//Tag proxy class is being created Cache.put (key, Pendinggenerationmarker); Break out of Loop has entered the creation process break; } while (true); 
  4. The class object that dynamically creates the proxy class. The first is to determine the package where the proxy class is located, the principle is as described above, if all are public interfaces, the package name is an empty string representing the top-level package, if all non-public interfaces are in the same package, the package name is the same as the package name of those interfaces, if there are multiple non-public interfaces and different packages, The exception is thrown to terminate the generation of the proxy class. Once the package has been determined, the class name of the proxy class is generated, as described in the "$ProxyN" form as mentioned earlier. The class name is also determined, and the next step is to witness the miracle happening-Dynamically generated proxy classes: listing 10. Dynamically generated proxy classes
    Dynamically generate an array of byte codes for the proxy class byte[] Proxyclassfile = Proxygenerator.generateproxyclass (proxyname, interfaces); try {     //dynamically define the newly generated proxy class    Proxyclass = DefineClass0 (loader, proxyname, proxyclassfile, 0,         Proxyclassfile.length); } catch (Classformaterror e) {     throw new IllegalArgumentException (E.tostring ())}//Record the class object of the generated proxy class into the proxyclasses table PR Oxyclasses.put (Proxyclass, NULL);

    Thus, all the code-generating work is done by the mysterious proxygenerator, and when you try to explore the class, you can only get the information that it is in the Sun.misc package that is not exposed, there are several constants, variables, and methods to complete this magical code generation process, but Sun did not provide the source code for reading. As for the definition of the dynamic class, it is executed by the Proxy's native static method DefineClass0.

  5. The code generation process goes to the end, updates the cache table based on the results, and if successful, updates the class object reference of the proxy class into the cache table, otherwise clearly caches the corresponding key values in the table, and finally wakes up all possible waiting threads.

After completing the above four steps, at this point, all the proxy class generation details have been introduced, the rest of the static methods such as Getinvocationhandler and Isproxyclass are so intuitive, just by querying the relevant variables can be done, so the code analysis of it is omitted.

The proxy class implements the deduction

Analysis of the source code of the proxy class, I believe in the reader's mind will form a clearer understanding of the Java dynamic agent mechanism, but, when the journey of exploration in the Sun.misc.ProxyGenerator class, all the mysteries converge at this time, I believe many readers will also on this The Proxygenerator class produces a similar puzzle: What exactly does it do? How does it generate the code for the dynamic proxy class? True, there is no definite answer here. Let us begin our journey of discovery with these doubts.

Things are often not as complex as they seem, and it is necessary that we be able to make them simple so that we may have more opportunities to dispel. Aside from all the unknown and complex mysteries of imagination, if you let us implement a proxy class in the simplest possible way, the only requirement is the same combination of dispatch and forwarding that calls the processor implementation method, what will your first reaction be? "It doesn't sound very complicated." Indeed, the work involved in pinching the calculation involves just a few reflection calls, as well as the boxing or unpacking process of the raw type data, and everything else seems to be the case. Very good, let us tidy up the thoughts, together to complete a complete deduction process.

Listing 11. Implementation of dispatch and forwarding deduction for method invocation in proxy class
Assume that the proxy interface is required Simulator public interface Simulator {short simulate (int arg1, long arg2, String arg3) throws Exceptiona, EXCEPTIONB;} Assuming that the proxy class is simulatorproxy, its class declaration will be as follows the final public class Simulatorproxy implements Simulator {//Invoke the reference to the Processor object protect         Ed Invocationhandler Handler;     The constructor that invokes the processor as a parameter public simulatorproxy (Invocationhandler handler) {This.handler = handler; }//Implement interface method simulate public short simulate (int arg1, long arg2, String arg3) throws Exceptiona, Except         IONB {///The first step is to get the method object of the simulate methods java.lang.reflect.Method way = null; try{method = Simulator.class.getMethod ("Simulate", new class[] {int.class, L        Ong.class, string.class}); } catch (Exception e) {//exception handling 1 (slightly)}//The second step is to call handler's Invoke method to dispatch a forwarding method call Obje         CT r = null;        try {r = Handler.invoke (This, method,         Boxing operation is required for raw type parameters new object[] {new Integer (arg1), New Long (ARG2), arg3}); }catch (Throwable e) {//exception handling 2 (slightly)}//The third step is to return the result (the return type is the original type and the unboxing operation is required) return ((short) R). s    Hortvalue (); } }

Simulation deduction in order to highlight the general logic so that more attention to the normal process, and the dilution of error handling, but in practice error handling is also very important. From the above deduction we can draw a very general structure of the process: the first step from the proxy interface to get the called method object, the second allocation method to call the processor execution, the third step to return the results. In this case, all the information is known, such as the interface name, method name, parameter type, return type, and the required boxing and unpacking operations, so if we do it by hand, then why don't you believe that Proxygenerator will not do similar implementations? At least this is a more likely implementation.

Let's turn our attention back to the previously diluted error handling. In exception handling 1, because we have reason to ensure that all information such as interface name, method name and parameter type are accurate, so this part of the probability of abnormal occurrence is basically zero, so the basic can be ignored. and exception handling 2, we need to think more. Recall that the interface method might declare support for an exception list, while calling the processor invoke method might throw exceptions that are not supported by the interface method, and recall that the Java dynamic agent mentioned earlier about exception handling features, for unsupported exceptions, must be thrown Undeclaredthrowableexception run-time exception. So by re-deducing, we can draw a clearer case of exception handling 2:

Listing 12. Fine-grained exception handling 2
Object r = null; try {     R = Handler.invoke (this,         method,         new object[] {new Integer (arg1), New Long (ARG2), arg3});} catch (Excep  Tiona e) {     //The interface method supports Exceptiona, can throw throw to    e;} catch (Exceptionb e) {     //interface method supports EXCEPTIONB, can throw    throw e; } catch (Throwable e) {     //Other unsupported exceptions, undeclaredthrowableexception     throw new Undeclaredthrowableexception (e) ; }

Thus we have completed the implementation of the dynamic proxy class deduction. The deduction implementation follows a relatively fixed pattern that can be applied to any interface that is defined, and the information required for code generation is known, and there is reason to believe that even the machine-generated code may continue to do so, at least to ensure that it is feasible.

Ointment

Admittedly, the proxy has been designed to be very graceful, but there is a little bit of regret, that is, it has always been unable to get rid of the shackles only support interface agent, because its design doomed this regret. Recall the inheritance diagram of dynamically generated proxy classes, which are already destined to have a common parent class called Proxy. Java's inheritance mechanism is doomed to these dynamic proxy classes can not implement the dynamic proxy class, because the multi-inheritance in Java is inherently unworkable.

There are a number of reasons why people can negate the need for class proxies, but there are also some reasons to believe that supporting a class dynamic agent will be better. Interface and class division, this is not very obvious, but in Java only to become so refined. If you only consider the declaration of a method and whether it is defined, there is a mixture of the two, its name is abstract class. The realization of the dynamic agent of the abstract class, believe also has its intrinsic value. In addition, there are some legacy classes that will never ever be associated with dynamic proxies because they do not implement any interfaces. So many, have to say is a small regret.

However, imperfect is not equal to not great, great is an essence, Java dynamic agent is an example.

Java design mode-proxy dynamic proxy mode

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.