Use Java Dynamic proxy to create wrapper

Source: Internet
Author: User

 


Java 1.3 introduces a new feature named "Dynamic Proxy Class", which can be used to dynamically create wrapper Classes for "implementations of known interfaces. Before the advent of version 1.3, when I first heard of the dynamic proxy class I was proposing, I thought it was just an eye-catching feature. Although it is a good thing to include it in a language, I cannot think of it for any practical use. With this insight, I tried to write a sample program with a dynamic proxy, but was surprised by its great power and immediately decided to put it in my toolbox, for future projects. Since then, I have been experiencing the benefits of it. It can always do what you want in the right way!

If there is no dynamic proxy

Before exploring the dynamic proxy class, let's take a look at what the dynamic proxy class looks like in some cases:

Public interface Robot {
Void moveTo (int x, int y );
Void workOn (Project p, Tool t );
}

Public class MyRobot implements Robot {
Public void moveTo (int x, int y ){
// Stuff happens here
}
Public void workOn (Project p, Tool t ){
// Optionally destructive stuff happens here
}
}

The code above demonstrates an interface named Robot and a general implementation of the interface named MyRobot. Suppose you want to intercept method calls to the MyRobot class (probably to limit the value of a parameter ).

Public class BuilderRobot implements Robot {
Private Robot wrapped;
Public BuilderRobot (Robot r ){
Wrapped = r;
}
Public void moveTo (int x, int y ){
Wrapped. moveTo (x, y );
}
Public void workOn (Project p, Tool t ){
If (t. isDestructive ()){
T = Tool. RATCHET;
}
Wrapped. workOn (p, t );
}
}

One way is to use an explicit package class, as shown above. The BuilderRobot class obtains a Robot in its constructor and intercepts the workOn method to ensure that the tools used in any project are not destructive. In addition, because the BuilderRobot package implements the Robot interface, a BuilderRobot instance can be used wherever a Robot can be used.

For this type of BuilderRobot, once you want to modify or expand the Robot interface, its shortcomings will be exposed. To add a method for the Robot interface, you must add a method for the BuilderRobot class. Add 10 methods to BuilderRobot. If BuilderRobot, CrusherRobot, SpeedyRobot, and SlowRobot are both Robot Wrapper Classes, you must add 10 methods for them respectively. This is obviously a very inefficient solution.

Public class BuilderRobot extends MyRobot {
Public void workOn (Project p, Tool t ){
If (t. isDestructive ()){
T = Tool. RATCHET;
}
Super. workOn (p, t );
}
}

The above code is another way to program BuilderRobot. Note that BuilderRobot is a subclass of MyRobot. This can solve the problem in the 2nd-segment code package solution. That is to say, you do not have to modify BuilderRobot to modify the Robot interface. However, this creates a new problem: only the MyRobot object can be BuilderRobot. Before that, any object implementing the Robot interface can become a BuilderRobot. Currently, the linear class parentage restrictions imposed by Java prohibits us from converting any Robot (ArbitraryRobot) into a BuilderRobot.

Dynamic proxy is also limited

Dynamic proxy combines the advantages of the above two solutions. When using dynamic proxies, you do not need to use an explicit packer for all methods for the encapsulated class you created, nor do you need to have strict origins for the subclass you created, either method is the best you think. However, dynamic proxy still has a limit. When you use a dynamic proxy, an interface must be implemented for the object to be packaged/extended. This interface defines all methods to be used in the package. The purpose of this restriction is to encourage good design, rather than bringing you more trouble. Based on experience, each class should implement at least one interface (nonconstant interface ). Good Interface Usage not only makes dynamic proxy possible, but also facilitates program modularization.

Use Dynamic proxy

The following code demonstrates the classes required to create a BuilderRobot using a dynamic proxy. Note that the BuilderRobotInvocationHandler class we created does not even implement the Robot interface. Instead, it implements java. lang. reflect. InvocationHandler and only provides one invoke method. Call any method on the proxy object through this method. Observe the invoke body and we find that it checks the name of the method to be called. If the name is workOn, the second parameter is switched to a non-destructive tool.

However, what we get is still an InvocationHandler with the invoke method, rather than the expected Robot object. The real charm of dynamic proxy can be reflected only when the actual Robot instance is created. In the source code, we do not define a Robot package or subclass. Even so, we can still get a dynamically created class. It calls the code snippet in the static method createbuilderrobotinvocationhandler in builderrobot, thus implementing the Robot interface and integrating the Builder tool filter.

Import java. lang. reflect. Proxy;
Import java. lang. reflect. InvocationHandler;
Import java. lang. reflect. Method;

Public class BuilderRobotInvocationHandler implements InvocationHandler {
Private Robot wrapped;
Public BuilderRobotInvocationHandler (Robot r ){
Wrapped = r;
}
Public Object invoke (Object proxy, Method method, Object [] args)
Throws Throwable {
If ("workOn". equals (method. getName ())){
Args [1] = Tool. RATCHET;
}
Return method. invoke (wrapped, args );
}
Public static Robot createBuilderRobot (Robot toWrap ){
Return (Robot) (Proxy. newProxyInstance (Robot. class. getClassLoader (),
New Class [] {Robot. class },
New BuilderRobotInvocationHandler (toWrap )));
}
Public static final void main (String [] args ){
Robot r = createBuilderRobot (new MyRobot ());
R. workOn ("scrap", Tool. CUTTING_TORCH );
}
}

The code in createBuilderRobot is complex on the surface, but its function is actually very simple. It tells the Proxy class to dynamically create an object using a specified class loader, this object implements the specified interface (in this example, Robot) and replaces the traditional method subject with the provided InvocationHandler. The result object returns true in an instanceof Robot test and provides methods that can be found in any class that implements the Robot interface.

Interestingly, there is no reference to the Robot interface in the invoke method of the BuilderRobotInvocationHandler class. InvocationHandlers is not dedicated to interfaces that provide "proxy method implementation" to them. You can write an InvocationHandler and use it as the backend of many proxy classes.
However, in this example, we provide another RobotInterface instance for BuilderRobotInvocationHandler in the form of constructor parameters. Any method call on the proxy Robot instance is ultimately entrusted to the "encapsulated" Robot by BuilderRobotInvocationHandler. However, although this is the most common design, you must understand that InvocationHandler does not have to be delegated to another instance of the interface to be proxies. In fact, InvocationHandler can provide the method subject on its own without a delegate target.

Note that if the Robot interface changes, the invoke method in BuilderRobotInvocationHandler will be slow. For example, if the workOn method is renamed, the non-destructive tool trap will quietly fail, and BuilderRobots may cause damage. It is easier to detect, but it does not necessarily cause problems. It is the workOn method's overloaded version. If the method has the same name but uses a different parameter list, a ClassCastException or ArrayIndexOutOfBoundsException exception may occur at runtime. To this end, the following code provides a solution that can generate a more flexible BuilderRobotInvocationHandler. In this Code, if you use a tool in any method at any time, the tool will be replaced with a non-destructive tool. Please try to use subclass processing or traditional Delegation for testing.

Import java. lang. reflect. Proxy;
Import java. lang. reflect. InvocationHandler;
Import java. lang. reflect. Method;

Public class BuilderRobotInvocationHandler implements InvocationHandler {
Private Robot wrapped;
Public BuilderRobotInvocationHandler (Robot r ){
Wrapped = r;
}
Public Object invoke (Object proxy, Method method, Object [] args)
Throws Throwable {
Class [] paramTypes = method. getParameterTypes ();
For (int I = 0; I <paramTypes. length; I ++ ){
If (Tool. class. isAssignableFrom (paramTypes [I]) {
Args [I] = Tool. RATCHET;
}
}
Return method. invoke (wrapped, args );
}
Public static Robot createBuilderRobot (Robot toWrap ){
Return (Robot) (Proxy. newProxyInstance (Robot. class. getClassLoader (),
New Class [] {Robot. class },
New BuilderRobotInvocationHandler (toWrap )));
}
Public static final void main (String [] args ){
Robot r = createBuilderRobot (new MyRobot ());
R. workOn ("scrap", Tool. CUTTING_TORCH );
}
}
Suggestions

In most development environments, tools are used to replace R

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.