![]() |
source: SAP community |
Dynamic Proxy mechanism is part of the standard Java SDK implementation under the java.lang.reflect package. This mechanism allows creating on the fly proxy implementation based on one or more interface types which can then be used interchangeably at runtime. This process allows leveraging many known patterns in code dynamically like interceptor, decorator, adapter, etc. In every case, the primary motive is to intercept the method calls and reroute or preprocess them to add or remove functional behaviors dynamically at runtime. Dynamic proxy classes are useful to an application or library that needs to provide type-safe reflective dispatch of invocations on objects that present public interfaces. To understand how proxying works it’s important to understand what the proxy pattern dictates.
The Proxy pattern – A Brief Review
The Proxy Pattern is an original Gang of Four (GOF) Structural pattern that was proposed way back in 1994 and was inducted in the Portland Pattern Repository subsequently. Theoretically, the Proxy pattern involves decomposing the original implementation target into two separate and distinct objects namely the stub (or surrogate) and the skeleton. Traditionally the stub objects reside on the client-side and provide what is called location transparency to the clients while the skeleton resides on the server-side and provides location transparency to the intended implementation targets. The idea of location transparency implies that both the clients and the target implementation are relieved of the responsibilities of knowing where the other one resides. The process of creating these stub and skeleton objects based on an implementation target is called proxying.
One very common example of remote proxying in standard Java is RMI. The Proxy pattern is used by Remote Method Invocation (RMI) to make an object executing in another JVM appear like a local object to its clients. This is the idea on which Enterprise JavaBeans (EJB) and CORBA work. Another example is JAX-RPC service invocation. While the clients deal with only the returning data values and the target service deals only with the processing and creation of the data values, it’s the proxy stubs and skeleton objects that do the dirty job of marshaling and unmarshalling the data, location identification of the target, service invocation, transaction demarcation, etc.
But all this is about remote proxying where it is deemed that the client and targets don’t necessarily reside within the same JVM boundary. Sometimes the target to be proxied lies in the same JVM in which the client does. In such scenarios, the actual full-blown proxy mechanism design shrinks considerably. Essentially the need for a skeleton object is lessened and in most cases deemed redundant and this also reduced the need for a stub or a surrogate. The proxy pattern gets reduced in such cases to nothing more than a simple decorator pattern implementation whose main aim may only be to induce an interceptor for 'pre or post' processing of invocation calls made on the target or to audit stuff amongst other things. Consequently, it is merely enough to introduce a single intermediate temporary implementation between the client and target which conforms to the target’s public interface contract and which the clients can thus use interchangeably in place of the actual target implementation type.
Today there are abundant provisions to generate such intermediate implementation both at compile time and runtime. Based on the use and complexity involved, some implementations tend to depend only on the runtime generation of stubs and skeletons e.g. EJBs at deployment time. Dynamic proxy mechanism allows for the creation of a proxy object at run time without generating stub classes at compile time. In JDK, the standard RMI facility now uses dynamic proxies instead of relying on generated stubs as it did earlier. Spring AOP is another elegant example that relies heavily on dynamic proxies to accomplish many feats in Aspect orientation dynamically at runtime.
The Dynamic Proxy API
The JDK dynamic Proxy relies heavily on interface based implementations. The idea is pretty simple and extends from the core Object Orientation philosophy of coding to an interface instead of an implementation. The basic presumption here is to believe that we may need proxying on heavy objects which do a lot of service processing for business logic or service discovery in a good well build enterprise application. Thus the need for interfaces is inevitable and also recommended for code scalability and maintainability. Based on this presumption the overall proxy API broadly breaks down to:
- A target implementation: the class we intend to proxy.
- A dynamic proxy class (simply referred to as a proxy class): a class that implements a list of interfaces specified at runtime. For smooth practical implementation, it is imperative that this proxy class implements at least one interface which the target implements. Though contractual obligations don’t dictate that this class should implement all the interfaces or should not implement new ones but down the processing line the proxy has to delegate the actual processing to the target sometime and one to one mapping between interface methods and implemented ones simplifies the overall design.
- Proxy interfaces: is the list of public interfaces that the proxy implementation should conform to. This list of interfaces is provided at runtime or declaratively.
- Proxy Instance: the runtime instance of the dynamically generated proxy class.
Creating a JDK Proxy Class – A Primer
Each JDK proxy class has one public constructor that takes one argument, an implementation of the interface InvocationHandler. Each proxy instance has an associated invocation handler object, the one that was passed to its constructor. Rather than having to use the reflection API to access the public constructor, a proxy instance can be also be created by calling the Proxy.newProxyInstance method, which combines the actions of calling Proxy.getProxyClass with invoking the constructor with an invocation handler.
First, we write our business interface which would be publically accessible to all clients.
....and a concrete service implementation which implements the IAbc interface.
Now we write a dynamic proxy class MyLoggerProxy which intercepts the actual calls to methods on IAbc implementations and print a start and end log messages.
To construct a MyLoggerProxy for an implementation of the IAbc interface and call one of its methods we need to use the dynamic proxy instead of the concrete class while keeping the reference bound to the interface IAbc.
IAbc abcObject = (IAbc) MyLoggerProxy.newInstance(
new
AbcImpl());
abcObject.greet("Test User");
Note that instead of just using new AbcImpl() we have injected a Dynamic Proxy on the right side. Since the left side of the invocation is typed to the interface instead of implementation, so we can dynamically replace the right side as and when we may need. Only thing to keep track of is to ensure that the actual target we intent to proxy implements the interface that we are trying to assign the proxy instance to.
Dynamic Proxy Considerations and Limitations
Dynamic proxies can reduce a lot of code that needs to be written to achieve the same level of functional flexibility as can be achieved with them. With a few hundred classes in our application trying to implement our simple example of MyLoggerProxy would prove to be a nightmare. As the classes in the overall application grow sequentially, the effort required to add such functionality grows exponentially. To understand how and when can a dynamic proxy help us we need to delve into JDK proxy contracts.
Understanding Proxy Instance Properties
By contract, a proxy has a given set of properties. Some of the important ones which programmers need to remember and design around are as follows:
- Proxy classes are always public and final and thus never abstract.
- The actual runtime name of a proxy class is unspecified. The VM space of class names that begin with the string $Proxy is reserved for proxy classes only.
- A proxy class must extend java.lang.reflect.Proxy
- A proxy class implements exactly the same number of interfaces specified at its creation and in the same given order. This is by convention and not a runtime/compile time limitation.
- If a proxy class implements a non-public interface, then the proxy should be defined in the same package scope as that interface. Otherwise, the package of a proxy class is also unspecified and results in the default package qualifier being used.
- Given a proxy instance abc and an interface IFoo which the proxy implements, the following would always return true:abc instanceof IFooand the following cast operation will succeed instead of throwing a ClassCastException
(IFoo)abc
- The static Proxy.getInvocationHandler method will return the invocation handler associated with the proxy instance passed as its argument. If the object passed to Proxy.getInvocationHandler is not a proxy instance, then an IllegalArgumentException will be thrown
- An invocation of the hashCode, equals, or toString methods declared in java.lang.Object on a proxy instance will be encoded and dispatched to the invocation handler’s invoke method in the same manner as interface method invocations are encoded and dispatched.
Methods Signatures Duplicated in Multiple Proxy Interfaces
As and when the application becomes bigger and complex, it's very much possible to end up with more than one interface having the same method signatures. When two or more interface types of a proxy class contain a method with the same method signature, the order of the proxy class’s interfaces becomes significant. When such a duplicate method is invoked on a proxy instance, the java.lang.Method object passed to the invocation handler may not necessarily be the one whose declaring class is assignable from the reference type of the interface that the proxy’s method was invoked through.
This limitation exists because the corresponding method implementation in the generated proxy class cannot determine which interface it was invoked through. Since this implementation is not accessible at compile time so a deferred runtime consideration is required. But then at runtime, it is not possible to locate the target interface type correctly to which this proxy method belongs. Therefore, when a duplicate method is invoked on a proxy instance, the Method object for the method in the foremost interface that contains the method (either directly or inherited through a super interface) in the proxy class’s list of interfaces, is passed to the invocation handler’s invoke method, regardless of the reference type through which the method invocation occurred. This limitation at times becomes a direct cause for transparent RuntimeExceptions and wrong logic processing issues. However, there are ways using annotation metadata to help the proxy implementation locate the actual exact interface type to which the method being called belongs.
Another special consideration is required for java.lang.Object class’s methods. It is possible that a proxy implementation contains a method with the same name and parameter signature as the hashCode, equals, or toString methods of java.lang.Object. When such a method is invoked on a proxy instance, the Method object passed to the invocation handler will have java.lang.Object as its declaring class. This means that the public, non-final methods of java.lang.Object logically precedes all of the proxy interfaces for the determination of which Method object to pass to the invocation handler.
Proxies also handled unknown checked exceptions rather differently in the duplicate method scenario. When a duplicate method is dispatched to an invocation handler, the invoke method should only throw checked exception types that are assignable to one of the exception types in the throws clause of the method in all of the available proxy interfaces that it can be invoked through. If the invoke method throws a checked exception that is not assignable to any of the exception types declared by the method in all of the proxy interfaces that it can be invoked through, then an unchecked UndeclaredThrowableException will be thrown by the invocation on the proxy instance. This restriction means that not all of the exception types returned by invoking getExceptionTypes on the java.lang.Method objects passed to the invoke method can readily be thrown successfully by the invoke method.
Serialization of Proxies
Since java.lang.reflect.Proxy implements java.io.Serializable, proxy instances can be serialized as just about any seralizable type in Java. If a proxy instance contains an invocation handler that is not assignable to java.io.Serializable, however, then a java.io.NotSerializableException will be thrown if such an instance is written to a java.io.ObjectOutputStream.
Similarly for proxy classes, implementing java.io.Externalizable, it has the same effect with respect to serialization as implementing java.io.Serializable viz: the writeExternal and readExternal methods of the Externalizable interface will never be invoked on a proxy instance (or an invocation handler) as part of its serialization process. As with all Class objects, the Class object for a proxy class is always serializable.
By design a proxy class never has any serializable fields and always has a serialVersionUID of 0L. This means that when the java.lang.Class object for a proxy class is passed to the static lookup method of java.io.ObjectStreamClass, the returned ObjectStreamClass instance will have the following behaviors:
- Invoking its getSerialVersionUID method will always return 0L.
- Invoking its getFields method will return an array of length zero.
- Invoking its getField method with any String argument will return null.To understand how the Serialization process identifies and constructs the proxy object back and forth we need to stem back to the type code offsets in the Object serialization stream protocol. The protocol for Object Serialization supports a type code named TC_PROXYCLASSDESC, which is a terminating symbol in the AST grammar for the stream format; its type and value are defined by the following constant field in the java.io.ObjectStreamConstants interface:
final static byte TC_PROXYCLASSDESC = (byte)0x7D;
- newClassDesc:
TC_PROXYCLASSDESC newHandle proxyClassDescInfo - proxyClassDescInfo:
(int)<count> proxyInterfaceName[count] classAnnotation superClassDesc
When an ObjectOutputStream serializes the class descriptor for a class that is a proxy class, as determined by passing its Class object to the Proxy.isProxyClass method, it uses the TC_PROXYCLASSDESC type code instead of TC_CLASSDESC as is normally used for regular java classes.
Now following the rules above, in the expansion of proxyClassDescInfo, the sequence of proxyInterfaceName items are the names of all of the interfaces implemented by the proxy class, in the order that they are returned by invoking the getInterfaces method on the Class object. The classAnnotation items are treated normally and hold the same meaning as they do for regular Classes. However for a proxy class, superClassDesc is the class descriptor for its own immediate superclass which is the java.lang.reflect.Proxy.
When an ObjectInputStream encounters the type code TC_PROXYCLASSDESC, it deserializes the class descriptor for a proxy class from the stream as described above. Instead of calling its resolveClass method to resolve the java.lang.Class object for the class descriptor of the proxy, the following method in java.io.ObjectOutputStream is called instead:
protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException;The list of interface names that were deserialized in the proxy class descriptor are passed as the interfaces argument to resolveProxyClass. The default implementation of resolveProxyClass in ObjectInputStream returns the results of calling Proxy.getProxyClass with the list of Class objects for the interfaces named in the interfaces parameter.
The Class object used for each interface name i is the value returned by calling:
Class.forName(i, false, loader)
where, loader is the first non-null class loader up the execution stack, or null if no non-null class loaders are on the stack.
If for any reason the Proxy.getProxyClass throws an IllegalArgumentException, resolveClass will throw a ClassNotFoundException containing that IllegalArgumentException inside it, as source. This is because in most probable cases such an exception would result for ClassNotFoundException cases only. Since a proxy class never has its own serializable fields, the classdata[] in the stream representation of a proxy instance consists wholly of the instance data for its superclass, java.lang.reflect.Proxy. In most cases the only serializable field that a proxy has is ‘h’, which contains the invocation handler for the proxy instance.
Parting Thoughts....
This post talked in depth about the proxy pattern and how you can build your own dynamic proxies using standard Java JDK without any external libraries. While dynamic proxies are very powerful features that can extend the creativity of your class type behaviours and help you use the polymorphism principle to the fullest, it does come with its own risks and considerations.
Comments
Post a Comment
Questions? Thoughts? Feedback? Corrections? Please do let us know. Thank you.