| home / internet / reflection / 1 | [previous][next] |
|
|
George has been assigned the task of creating tracing versions of several of the classes that he maintains. In a tracing class, each method records information about its entry and, after method execution, records information about its return. George's employer, WCI, wants tracing available for their classes because tracing helps with problem determination in deployed software.
Consider the following scenario. A customer calls WCI technical support with a defect report. Tech support asks the customer to turn tracing on in their software and follow the steps to reproduce the defect. Because tracing is turned on, the customer can then send WCI a file containing the path through the WCI source code.
This information solves many problems for the WCI technical team. It tells them a great deal about the state of the program during the failure. It also may prevent them from having to replicate their customer's environment and data. While tracing is a useful feature, it is also very I/O intensive. Therefore, classes should be able to turn tracing on and off. However, including tracing code and guards to turn it on and off in each class bloats the classes and makes them slower because of the execution of the if statements. Due to these constraints, George decides to make tracing and nontracing versions of his classes.
One option George considers is subclassing each nontraced class and overriding each method with traces and super calls. He can then set up a process for either instantiating the traced or nontraced version depending upon some command-line argument. George quickly realizes that this option has the following shortcomings:
Clearly, George is in need of a better solution. George needs to separate the concern
of tracing from the rest of the source code and implement it in a separate
module. George reasons that this can be done with a proxy, where the proxy
traces the call before and after delegating the method invocation to the target.
Although there will be one proxy object for every target, with the use of reflection,
all of the proxies can be instances of one proxy class, which addresses the
shortcomings raised previously. Before presenting George's solution, let's examine
java.lang.reflect.Proxy.
As stated previously, the two important tasks for any proxy are interface implementation and delegation. The Java Proxy class accomplishes implementation of interfaces by dynamically creating a class that implements a set of given interfaces. This dynamic class creation is accomplished with the static getProxyClass and newProxyInstance factory methods, shown in listing 4.1.
Listing 4.1 Partial declaration for java.lang.reflect.Proxy
Each class constructed by these factory methods is a public final subclass of Proxy, referred to as a proxy class. We refer to an instance of one of these dynamically constructed proxies as a proxy instance. We call the interfaces that the proxy class implements in this way proxied interfaces. A proxy instance is assignmentcompatible with all of its proxied interfaces.
The getProxyClass method retrieves the proxy class specified by
a class loader and an array of interfaces. If such a proxy class does not exist,
it is dynamically constructed. Because each Java class object is associated
with a class loader, in order to dynamically create a proxy class, getProxyClass
must have a class loader parameter (the reason for this requirement is explained
in chapter 6). The name of each proxy class begins with $Proxy
followed by a number, which is the value of an index that is increased each
time a proxy class is created.
All proxy classes have a constructor that takes an InvocationHandler
parameter. InvocationHandler is an interface for objects that handle
methods received by proxy instances through their proxied interfaces. We discuss
invocation handlers further after we finish with the methods of Proxy.
A combination of getConstructor and newInstance may
be used to construct proxy instances, as in the following lines
Proxy cl = getProxyClass( SomeInterface.getClassLoader(),
Class[]{SomeInterface.class}
);
Constructor cons = cl.getConstructor( new Class[]{InvocationHandler.class}
);
Object proxy = cons.newInstance( new Object[]
{ new SomeIH( obj ) } );
where SomeIH is a class that implements InvocationHandler. Alternatively, this
sequence can be accomplished with a single call to newProxyInstance:
Object proxy = Proxy.newProxyInstance( SomeInterface.getClassLoader(),
Class[]{SomeInterface.class},
new SomeIH(
obj ) );
This call implicitly creates the proxy class, which can be retrieved with getProxy-
Class.
The static method isProxyClass is used to determine if a class object represents
a proxy class. The line
Proxy.isProxyClass(obj.getClass())
may be used to determine if obj refers to a proxy instance. If p refers to a proxy
instance,
Proxy.getInvocationHandler(p)
returns the InvocationHandler that was used to construct p.
| home / internet / reflection / 1 | [previous][next] |
Created: March 27, 2003
Revised: November 12, 2004
URL: http://webreference.com/internet/reflection/1