Go to: Java Dynamic proxy mechanism analysis and extension

Source: Internet
Author: User
Tags unsupported

Introduction

The emergence of Java Dynamic proxy mechanism makes Java
Developers do not need to write the proxy class manually. They can dynamically obtain the proxy class by simply specifying a set of interfaces and delegate class objects. The proxy class is responsible for allocating all method calls to the delegate object for reflection and execution.
During execution, developers can also adjust the delegate class objects and their functions as needed. This is a flexible and flexible proxy framework. By reading this article, you will
A more in-depth understanding of the dynamic proxy mechanism. This article first analyzes the Java Dynamic proxy's operating mechanism and features, and deducts the internal implementation of the dynamic generation class.

Back to Top

Proxy: Design Mode

Proxy is a common design pattern, which aims to provide a proxy for other objects to control access to an object. The proxy class is responsible for preprocessing messages for the delegate class, filtering messages and forwarding messages, and subsequent processing after the message is executed by the delegate class.

Figure 1. Proxy Mode

To maintain behavior consistency, the proxy class and the delegate class usually implement the same interface, so there is no difference between the two in the visitor's opinion. Through the intermediary layer of the proxy class, the delegate can be effectively controlled
Direct access to class objects can also well hide and protect delegate objects, and also reserve space for implementing different control policies, thus gaining greater flexibility in design. Java
The dynamic proxy mechanism almost perfectly practices the design concept of the proxy mode in a clever way.

Back to Top

Related Classes and interfaces

To understand the Java Dynamic proxy mechanism, you must first understand the following related classes or interfaces:

  • Java. Lang. Reflect. Proxy: This is the main class of the Java Dynamic proxy mechanism. It provides a set of static methods to dynamically generate proxy classes and objects for a group of interfaces.

    Listing 1. Static proxy method


    // Method 1: This method is used to obtain the call processor associated with the specified proxy object.
    Static invocationhandler getinvocationhandler (Object proxy)

    // Method 2: This method is used to obtain the class object associated with the dynamic proxy class of the specified class loader and a group 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 instance for a specified class loader, a group of interfaces, and a call processor.
    Static object newproxyinstance (classloader loader, class [] interfaces,
    Invocationhandler H)
  • Java. lang. reflect. invocationhandler: this is the call processor interface. It customizes an invoke Method for centralized processing of method calls on dynamic proxy objects. Generally, this method implements proxy access to the delegate class.

    Listing 2. Core methods of invocationhandler


    // This method is used to centrally process all method calls in the dynamic proxy class. The first parameter is both a proxy instance and the second parameter is the called method object.
    // The third method is to call the parameter. The call Processor Pre-processes the three parameters or assigns them to the delegated instance for launch and execution.
    Object invoke (Object proxy, method, object [] ARGs)

    Each time a dynamic proxy class object is generated, you must specify a processor object for calling the interface (see the third parameter of proxy static method 4 ).

  • Java. Lang. classloader: This Class Loader class is responsible for loading the class bytecode to Java
    Virtual Machine (JVM) and define class objects for it before the class can be used. Proxy
    Static Method to generate a dynamic proxy class also needs to be loaded by the class loader for use. The only difference between the dynamic proxy class and the normal class is that its bytecode is
    Dynamically generated at runtime rather than pre-stored in any. Class file.

    Each time a dynamic proxy class object is generated, a Class Loader object needs to be specified (see the first parameter of proxy static method 4)

Back to Top

Proxy Mechanism and Its Features

First, let's take a look at how to use Java Dynamic proxy. The procedure is as follows:

  1. Create your own call processor through the invocationhandler interface;
  2. You can specify a classloader object and a group of interfaces for the proxy class to create a dynamic proxy class;
  3. Obtain the constructor of the dynamic proxy class through the reflection mechanism. The unique parameter type is the type of the called processor interface;
  4. Create a dynamic proxy instance by using the constructor. During the constructor, the processor object is called as a parameter and passed in.

Listing 3. Dynamic proxy object creation process


// Invocationhandlerimpl implements the invocationhandler interface and distributes and forwards method calls from the proxy class to the delegate class.
// It usually contains a reference pointing to the delegate class instance, which is used to really execute the method call forwarded by the dispatch.
Invocationhandler handler = new invocationhandlerimpl (..);

// Use proxy to dynamically create proxy class objects for a group of interfaces, including interface Interfaces
Class clazz = proxy. getproxyclass (classloader, new class [] {interface. Class ,...});

// Obtain the constructor object from the generated class object through reflection
Constructor = clazz. getconstructor (new class [] {invocationhandler. Class });

// Create a dynamic proxy instance through the constructor object
Interface proxy = (Interface) constructor. newinstance (new object [] {handler });

The actual use process is simpler. Because the static proxy method newproxyinstance has encapsulated steps 2 to 4 for us, the simplified process is as follows:

Listing 4. Simplified process of creating dynamic proxy objects


// Invocationhandlerimpl implements the invocationhandler interface and distributes and forwards method calls from the proxy class to the delegate class.
Invocationhandler handler = new invocationhandlerimpl (..);

// Directly create a dynamic proxy instance through proxy
Interface proxy = (Interface) proxy. newproxyinstance (classloader,
New Class [] {interface. Class },
Handler );

Next, let's take a look at some features of the Java Dynamic proxy mechanism.

First, some characteristics of the dynamically generated proxy class itself. 1) package: if all the proxy interfaces are public
It will be defined in the top-level package (that is, the package path is empty). If the proxy interface has a non-public interface (because the interface cannot be defined as protect or
Private, so in addition to public, it is the default package access level), then it will be defined in the package where the interface is located (assuming that the proxy
A non-public interface A in the com. IBM. developerworks package, the newly generated proxy class is located in the package
Com. IBM. developerworks). The purpose of this design is to maximize the guarantee that dynamic proxy classes will not be successfully defined and accessed due to packet management issues; 2) Class repair
Modifier: the proxy class has final and public
Modifier, which means it can be accessed by all classes, but cannot be inherited again; 3) Class Name: The format is "$ proxyn", where n is an incremental Arabic number, representative
The dynamic proxy class generated n times. It is worth noting that not every time you call the proxy's static method to create a dynamic proxy class, n
The reason is that if you try to repeatedly create a dynamic proxy class for the same group of interfaces (including interfaces in the same order), it will intelligently return the Class Object of the previously created proxy class, instead of trying to create
Create a new proxy class to save unnecessary code generation and improve the efficiency of creating the proxy class. 4) class inheritance relationship: the inheritance relationship of the class

Figure 2. Inheritance diagram of dynamic proxy classes

As shown in the figure, the proxy class is its parent class. This rule applies to all dynamic proxy classes created by the proxy. This class also implements a set of interfaces It proxies, which is the root reason why it can be securely converted to an interface it proxies.

Next, let's take a look at some of the characteristics of proxy instances. Each instance is associated with a calling processor object. You can use the static method provided by proxy.
Getinvocationhandler
Obtain the call processor object of the proxy instance. When a proxy instance calls the methods declared in its proxy interface, these methods will eventually be called by the invoke
Method execution. In addition, it is worth noting that three methods in the Java. Lang. Object root class of the proxy class will also be assigned to the invoke of the calling processor.
Method execution. They are hashcode, equals, and tostring. Possible reasons are as follows: 1. These methods are public and not final.
Type, which can be covered by the proxy class. Second, because these methods often present a feature attribute of a class and have a certain degree of discrimination, in order to ensure the external consistency between the proxy class and the delegate class, the three methods should also be
This is assigned to the delegate class for execution. When a group of interfaces of the Contemporary Mechanism have repeated declarations and the method is called, the proxy class always obtains the method object from the interface at the top and assigns it to the call processor, regardless of the proxy
Whether the class instance is being referenced externally in the form of this interface (or a sub-interface inherited from this interface), because the current referenced type cannot be distinguished within the proxy class.

Next, let's take a look at the characteristics of a group of interfaces that are proxies. First, note that there are no repeated interfaces to avoid compilation errors when dynamic proxy code is generated. Second, these interfaces are applicable
The Class Loader must be visible; otherwise, the class loader will not be able to link them, and the class definition will fail. Again, all non-public
The interface must be in the same package, otherwise the proxy class will fail to be generated. Finally, the number of interfaces cannot exceed 65535, which is the limit set by JVM.

Finally, let's take a look at the features of exception handling. From calling the method declared by the processor interface, we can see that theoretically it can throw any type of exception, because all exceptions inherit from
Throwable
But is that true? The answer is no, because we must follow an inheritance principle: that is, when the subclass overwrites the parent class or implements the parent interface method, the thrown exception must be listed in the exception list supported by the original method.
. Therefore, although the calling processor can theoretically be used, it is often restricted unless the methods in the parent interface support throwing throwable exceptions. If
The method does produce exceptions that are not supported in the interface method declaration. What should I do? Rest assured that the Java Dynamic proxy class has already designed a solution for us: it will throw
Undeclaredthrowableexception. This exception is a runtimeexception.
Type, so it does not cause compilation errors. The getcause method of the exception can also obtain the unsupported exception object for error diagnosis.

Back to Top

Code is the best teacher

The mechanism and features are introduced. Next, let's use the source code to understand how proxy is implemented.

First, remember several important static variables of proxy:

Listing 5. Important static variables of proxy


// Ing table: used to maintain the proxy class cache for the Class Loader object
Private Static map loadertocache = new weakhashmap ();

// Tag: used to mark that a dynamic proxy class is being created
Private Static object pendinggenerationmarker = new object ();

// Synchronization table: records the created dynamic proxy class types, which are mainly determined by the isproxyclass method.
Private Static map proxyclasses = collections. synchronizedmap (New weakhashmap ());

// Associated call processor reference
Protected invocationhandler h;

Then, let's take a look at the proxy construction method:

Listing 6. Proxy Constructor


// Because the proxy never calls the constructor directly, the private type means that no call is allowed.
Private proxy (){}

// Since the proxy never calls the constructor directly, protected means that only the subclass can call the constructor.
Protected proxy (invocationhandler h) {This. H = H ;}

Next, you can quickly browse the newproxyinstance method because it is quite simple:

Listing 7. Proxy static method newproxyinstance


Public static object newproxyinstance (classloader loader,
Class <?> [] Interfaces,
Invocationhandler H)
Throws illegalargumentexception {

// Check if H is not empty; otherwise, an exception is thrown.
If (H = NULL ){
Throw new nullpointerexception ();
}

// Obtain the proxy class object associated with the specified class loader and a set of interfaces
Class Cl = getproxyclass (loader, interfaces );

// Obtain the constructor object through reflection and generate a proxy 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 ());
}
}

It can be seen that the real key of dynamic proxy is getproxyclass.
This method dynamically generates proxy class objects for a group of interfaces. Inside this method, you can see the proxy
All the heroes (static variables) in the game are available. Can't you wait? Let's take a look at proxy's most mysterious Hall. This method can be divided into four steps:

  1. Perform security checks on this set of interfaces to a certain extent, including checking whether the Interface Class Object is visible to the class loader and is exactly the same as the Interface Class Object recognized by the class loader, check to make sure that
    Interface Type instead of class type. This step is completed through a loop. After the check is passed, a string array containing all interface names will be obtained, which is recordedString[] interfaceNames. In general, this part of implementation is relatively intuitive, so most of the Code is omitted, and only the relevant code that determines whether a class or interface is visible to a specific class loader is reserved.

    Listing 8. Using the class. forname Method to Determine interface visibility


    Try {
    // Specify the Interface Name and Class Loader object, and set initializeboolean to false to indicate that class initialization is not required.
    // If the method returns normal, this indicates visible; otherwise, the classnotfoundexception exception will be thrown, indicating invisible.
    Interfaceclass = Class. forname (interfacename, false, loader );
    } Catch (classnotfoundexception e ){
    }
  2. Obtain the cache table corresponding to the Class Loader object as the keyword from the loadertocache ing table. If it does not exist, create a new cache table and update it
    Loadertocache. The cache table is a hashmap
    Instance. Normally, it stores key-value pairs (Interface Name List, dynamically generated proxy class Class Object reference ). When the proxy class is being created, it will be temporarily saved (interface name column
    Table, pendinggenerationmarker ). Mark pendinggenerationmarke
    The role is to Notify subsequent requests of the same type (the same interface array and the same group interface order) that the proxy class is being created. Please wait until the creation is complete.

    Listing 9. Use of cache tables


    Do {
    // Obtain the corresponding cache value using the Interface Name List as the keyword
    Object value = cache. Get (key );
    If (value instanceof reference ){
    Proxyclass = (class) (reference) value). Get ();
    }
    If (proxyclass! = NULL ){
    // If a file has been created, return directly
    Return proxyclass;
    } Else if (value = pendinggenerationmarker ){
    // The proxy class is being created. keep waiting.
    Try {
    Cache. Wait ();
    } Catch (interruptedexception e ){
    }
    // Wait until it is awakened, continue the loop, and pass the secondary check to ensure the creation is complete, otherwise wait again
    Continue;
    } Else {
    // Mark that the proxy class is being created
    Cache. Put (Key, pendinggenerationmarker );
    // The Break jump out of the loop has entered the creation process.
    Break;
    } While (true );
  3. Dynamically create a class object for the proxy class. First, determine the package where the proxy class is located. The principle is as stated above. If both are public
    Interface, the package name is a null string to indicate 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 these interfaces; if multiple non-public
    API and different packages, an exception is thrown to terminate the generation of the proxy class. After the package is determined, the Class Name of the proxy class is generated, as described in "$ proxyn. The class name is also determined. Next, see
    Witness the occurrence of miracles-dynamically generate a proxy class:

    Listing 10. dynamically generate proxy classes


    // Dynamically generate the byte code array of the proxy class
    Byte [] proxyclassfile = proxygenerator. generateproxyclass (proxyname, interfaces );
    Try {
    // Dynamically define the new proxy class
    Proxyclass = defineclass0 (loader, proxyname, proxyclassfile, 0,
    Proxyclassfile. Length );
    } Catch (classformaterror e ){
    Throw new illegalargumentexception (E. tostring ());
    }

    // Record the generated proxy class object into the proxyclasses table
    Proxyclasses. Put (proxyclass, null );

    It can be seen that all the code generation work is performed by the mysterious proxygenerator.
    After that, when you try to explore this class, all you can get is that it is located in the undisclosed sun. Misc.
    Package, there are several constants, variables and methods to complete this magic code generation process, but sun does not provide source code for study. As for the definition of dynamic classes,
    The native static method defineclass0 is executed.

  4. The code generation process ends and updates the cache table based on the results. If the result succeeds, the Class Object of the proxy class will be referenced in the cache table. Otherwise, the key values in the cache table will be clear, finally, wake up all possible waiting threads.

After completing the above four steps, all the agent class generation details have been introduced, and the remaining static methods such as getinvocationhandler and isproxyclass are so intuitive, you only need to query the relevant variables, so the code analysis is omitted.

Back to Top

Proxy class deduction

After analyzing the source code of the proxy class, I believe that readers will have a clearer understanding of the Java Dynamic proxy mechanism in their minds. However, when exploring
The sun. Misc. proxygenerator class suddenly stops, and all the mysteries are gathered at this time. I believe many readers will also consider this proxygenerator
Class has a similar question: what did it do? How does it generate dynamic proxy code? It is true that no definite answer is provided here. Let's start exploring together with these questions
Right.

Things are often not as complex as they seem. What we need is that we can simplify things so that we may have more opportunities to see the cloud. Aside from all the unknown and complex mysterious factors in our imagination
We use the simplest method to implement a proxy class. The only requirement is to call the dispatch and forwarding method of the processor implementation method. What is your first response? "It does not seem very complicated ". Indeed,
The computation involves only a few reflection calls, as well as the packing or unpacking process of raw data. Others seem to have come to the fore. Very good. Let's sort out our thoughts and complete them together.
A complete deduction process.

Listing 11. Distributed forwarding deduction for method calls in the proxy class


// Assuming proxy interface simulator is required
Public interface simulator {
Short simulate (INT arg1, long arg2, string arg3) throws partition tiona, exceptionb;
}

// Assume that the proxy class is simulatorproxy, and its class declaration will be as follows:
Final public class simulatorproxy implements simulator {

// Call the reference of the processor object
Protected invocationhandler handler;

// Constructor with the call processor as the parameter
Public simulatorproxy (invocationhandler handler ){
This. Handler = handler;
}

// Implement the interface method simulate
Public short simulate (INT arg1, long arg2, string arg3)
Throws segment tiona, exceptionb {

// The first step is to obtain the method object of the simulate method.
Java. Lang. Reflect. Method method = NULL;
Try {
Method = simulator. Class. getmethod (
"Simulate ",
New Class [] {Int. Class, long. Class, String. Class });
} Catch (exception e ){
// Exception Handling 1 (omitted)
}

// Step 2: Call the handler's invoke method to dispatch the forwarding method call
Object r = NULL;
Try {
R = handler. Invoke (this,
Method,
// Bind the original type parameter
New object [] {New INTEGER (arg1), new long (arg2), arg3 });
} Catch (throwable e ){
// Exception Handling 2 (omitted)
}
// The third step is to return the result (if the return type is the original type, binning is required)
Return (short) R). aggregate value ();
}
}

In order to highlight the general logic, the simulated deduction pays more attention to the normal process, but reduces error handling. In practice, error handling is equally important. From the above deduction, we can get a very
General structured process: the first step is to obtain the called method object from the proxy interface, the second step is to assign the method to call the processor for execution, and the third step returns the result. In this case, all information can be known, such
The Interface Name, method name, parameter type, return type, and the required packing and unpacking operations. So since we write them manually, why do we not trust proxygenerator?
Won't we do similar implementations? At least this is a more likely implementation.

Next, let's turn our attention back to the previously deprecated error handling. In exception handling 1
Because we have reason to ensure that all information, such as the interface name, method name, and parameter type, is accurate, so the probability of this part of the exception is basically zero, so it can be ignored. Exception Handling 2
We need to think more. Recall that the interface method may declare that an exception list is supported, while the invoke processor is called.
The method may throw an exception that is not supported by the interface method. Let's look back at the features of the Java Dynamic proxy mentioned earlier about Exception Handling. For unsupported exceptions, You must throw
Undeclaredthrowableexception exception. So through deduction, we can get a clearer case of exception handling 2:

Listing 12. Detailed Exception Handling 2


Object r = NULL;

Try {
R = handler. Invoke (this,
Method,
New object [] {New INTEGER (arg1), new long (arg2), arg3 });

} Catch (effectiona e ){

// The interface method supports javastiona and can be throw
Throw E;

} Catch (exceptionb e ){
// The interface method supports exceptionb and can be throw
Throw E;

} Catch (throwable e ){
// If other exceptions are not supported, the undeclaredthrowableexception will be thrown.
Throw new undeclaredthrowableexception (E );
}

This completes the deduction and Implementation of the dynamic proxy class. The deduction implementation follows a relatively fixed pattern and can be applied to any defined interface, and the information required for code generation is known, there is a reason to believe that even the code automatically written by the machine may continue this style, at least to ensure that this is feasible.

Back to Top

Weaknesses in the US

It is true that the proxy has been designed very beautifully, but there is still a little bit of regret, that is, it is always unable to get rid of supporting only interface
Proxy, because its design is doomed to this regret. Think back to the inheritance relationships of the dynamically generated proxy classes. They are already destined to have a common parent class called proxy. Java
The Inheritance Mechanism is doomed to the failure of these dynamic proxy classes to implement the dynamic proxy for the class, because multi-inheritance is essentially not feasible in Java.

There are many reasons for denying the necessity of class proxy, but there are also some reasons for believing that class proxy is supported.
Dynamic proxy will be better. The division of interfaces and classes is not very obvious, but only in Java
So detailed. 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 dynamic proxy for abstract classes also has its inherent value. This
In addition, there are some historical classes that will never be associated with dynamic proxies because no interfaces are implemented. All these are a small pity.

However, imperfection does not mean greatness. Greatness is an essence. Java Dynamic proxy is an example.

References

  • "Dynamic proxy classes": view relevant documents of Java Dynamic proxy.

  • "Introduction to Java Exception Handling": describes how to handle Java exceptions.
  • "Java Theory and Practice: modifying with dynamic proxies" (developerworks, September 2005): the dynamic proxy tool is Java. lang. add a part of the reflect package to JDK 1.3, which allows the program to create proxy objects. In this article, the author introduces several applications for dynamic proxy.
  • "Java verification with dynamic proxies" (developerworks, September 2004): This article shows you how dynamic proxies allow core application code to be independent of verification routines and focus only on business logic.
  • Developerworks Java Technology Zone: hundreds of articles on various aspects of Java programming.

Author Profile

Wang Zhongping, software engineer, is currently working at IBM Shanghai China System Technology Lab.

He Ping, software engineer, is currently working at IBM Shanghai China System Technology Lab.

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.