http://docs.oracle.com/javase/1.4.2/docs/guide/reflection/proxy.html
Contents
Introduction
Dynamic Proxy API
Serialization
Examples
Introduction
A dynamic proxy class is a class that implements a list ofinterfaces specified at runtime such that a method invocation throughone of the interfaces on an instance of the class will be encoded anddispatched to another object through a uniform interface. Thus, adynamic proxy class can be used to create a type-safe proxy object fora list of interfaces without requiring pre-generation of the proxyclass, such as with compile-time tools. Method invocations on aninstance of a dynamic proxy class are dispatched to a single method inthe instance's invocation handler, and they are encoded with a
java.lang.reflect.Method
object identifying the methodthat was invoked and an array of typeObject
containingthe arguments.Dynamic proxy classes are useful to an application or library thatneeds to provide type-safe reflective dispatch of invocations onobjects that present interface APIs. For example, an application canuse a dynamic proxy class to create an object that implements multiplearbitrary event listener interfaces-- interfaces that extend
java.util.EventListener
-- to process a variety of eventsof different types in a uniform fashion, such as by logging all suchevents to a file.
Dynamic Proxy Class API
A dynamic proxy class (simply referred to as a proxyclass below) is a class that implements a list of interfacesspecified at runtime when the class is created.
A proxy interface is such an interface that is implementedby a proxy class.
A proxy instance is an instance of a proxy class.
Creating a Proxy Class
Proxy classes, as well as instances of them, are created using thestatic methods of the class java.lang.reflect.Proxy.
The
Proxy.getProxyClass
method returns thejava.lang.Class
object for a proxy class given a classloader and an array of interfaces. The proxy class will be defined inthe specified class loader and will implement all of the suppliedinterfaces. If a proxy class for the same permutation of interfaceshas already been defined in the class loader, then the existing proxyclass will be returned; otherwise, a proxy class for those interfaceswill be generated dynamically and defined in the class loader.There are several restrictions on the parameters that may be passedto
Proxy.getProxyClass
:
- All of the
Class
objects in theinterfaces
array must represent interfaces, not classes or primitive types.- No two elements in the
interfaces
array may refer toidenticalClass
objects.- All of the interface types must be visible by name through thespecified class loader. In other words, for class loader
cl
and every interfacei
, the followingexpression must be true:Class.forName(i.getName(), false, cl) == i- All non-public interfaces must be in the same package; otherwise,it would not be possible for the proxy class to implement all of theinterfaces, regardless of what package it is defined in.
- No two interfaces may each have a method with the same name andparameter signature but different return type.
- The resulting proxy class must not exceed any limits imposed onclasses by the virtual machine. For example, the VM may limit thenumber of interfaces that a class may implement to 65535; in thatcase, the size of the
interfaces
array must not exceed65535.If any of these restrictions are violated,
Proxy.getProxyClass
will throw anIllegalArgumentException
. If theinterfaces
array argument or any of its elements arenull
, aNullPointerException
will be thrown.Note that the order of the specified proxy interfaces issignificant: two requests for a proxy class with the same combinationof interfaces but in a different order will result in two distinctproxy classes. Proxy classes are distinguished by the order of theirproxy interfaces in order to provide deterministic method invocationencoding in cases where two or more of the proxy interfaces share amethod with the same name and parameter signature; this reasoning isdescribed in more detail in the section below titled Methods Duplicated in Multiple Proxy Interfaces.
So that a new proxy class does not need to be generated each time
Proxy.getProxyClass
is invoked with the same class loaderand list of interfaces, the implementation of the dynamic proxy classAPI should keep a cache of generated proxy classes, keyed by theircorresponding loaders and interface list. The implementation shouldbe careful not to refer to the class loaders, interfaces, and proxyclasses in such a way as to prevent class loaders, and all of theirclasses, from being garbage collected when appropriate.Proxy Class Properties
A proxy class has the following properties:
- Proxy classes are public, final, and not abstract.
- The unqualified name of a proxy class is unspecified. The spaceof class names that begin with the string
"$Proxy"
is,however, to be reserved for proxy classes.- A proxy class extends
java.lang.reflect.Proxy
.- A proxy class implements exactly the interfaces specified at itscreation, in the same order.
- If a proxy class implements a non-public interface, then it willbe defined in the same package as that interface. Otherwise, thepackage of a proxy class is also unspecified. Note that packagesealing will not prevent a proxy class from being successfully definedin a particular package at runtime, and neither will classes alreadydefined in the same class loader and the same package with particularsigners.
- Since a proxy class implements all of the interfaces specified atits creation, invoking
getInterfaces
on itsClass
object will return an array containing the samelist of interfaces (in the order specified at its creation), invokinggetMethods
on itsClass
object will returnan array ofMethod
objects that include all of themethods in those interfaces, and invokinggetMethod
willfind methods in the proxy interfaces as would be expected.- The
Proxy.isProxyClass
method will return true if itis passed a proxy class-- a class returned byProxy.getProxyClass
or the class of an object returned byProxy.newProxyInstance
-- and false otherwise. Thereliability of this method is important for the ability to use it tomake security decisions, so its implementation should not just test ifthe class in question extendsjava.lang.reflect.Proxy
.- The
java.security.ProtectionDomain
of a proxy classis the same as that of system classes loaded by the bootstrap classloader, such asjava.lang.Object
, because the code for aproxy class is generated by trusted system code. This protectiondomain will typically be grantedjava.security.AllPermission
.Creating a Proxy Instance
Each 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 usethe reflection API to access the public constructor, a proxy instancecan be also be created by calling the
Proxy.newProxyInstance
method, which combines the actionsof callingProxy.getProxyClass
with invoking theconstructor with an invocation handler.Proxy.newProxyInstance
throwsIllegalArgumentException
for the same reasons thatProxy.getProxyClass
does.Proxy Instance Properties
A proxy instance has the following properties:
- Given a proxy instance
proxy
and one of theinterfaces implemented by its proxy classFoo
, thefollowing expression will return true:and the following cast operation will succeed (rather than throwingaproxy instanceof Foo
ClassCastException
):(Foo) proxy
- The static
Proxy.getInvocationHandler
method willreturn the invocation handler associated with the proxy instancepassed as its argument. If the object passed toProxy.getInvocationHandler
is not a proxy instance, thenanIllegalArgumentException
will be thrown.- An interface method invocation on a proxy instance will beencoded and dispatched to the invocation handler's
invoke
method as described below.The proxy instance itself will be passed as the first argument of
invoke
, which is of typeObject
.The second argument passed to
invoke
will be thejava.lang.reflect.Method
instance corresponding to theinterface method invoked on the proxy instance. The declaring classof theMethod
object will be the interface that themethod was declared in, which may be a superinterface of the proxyinterface that the proxy class inherits the method through.The third argument passed to
invoke
will be an arrayof objects containing the values of the arguments passed in the methodinvocation on the proxy instance. Arguments of primitive types arewrapped in an instance of the appropriate primitive wrapper class,such asjava.lang.Integer
orjava.lang.Boolean
. The implementation of theinvoke
method is free to modify the contents of thisarray.The value returned by the
invoke
method will becomethe return value of the method invocation on the proxy instance. Ifthe declared return value of the interface method is a primitive type,then the value returned byinvoke
must be an instance ofthe corresponding primitive wrapper class; otherwise, it must be atype assignable to the declared return type. If the value returned byinvoke
isnull
and the interface method'sreturn type is primitive, then aNullPointerException
will be thrown by the method invocation on the proxy instance. If thevalue returned byinvoke
is otherwise not compatible withthe method's declared return type as described above, aClassCastException
will be thrown by the proxy instance.If an exception is thrown by the
invoke
method, itwill be also thrown by the method invocation on the proxy instance.The exception's type must be assignable to either any of the exceptiontypes declared in the signature of the interface method or to theunchecked exception typesjava.lang.RuntimeException
orjava.lang.Error
. If a checked exception is thrown byinvoke
that is not assignable to any of the exceptiontypes declared in thethrows
clause of the interfacemethod, then anUndeclaredThrowableException
will bethrown by the method invocation on the proxy instance. TheUndeclaredThrowableException
will be constructed withthe exception that was thrown by theinvoke
method.- An invocation of the
hashCode
,equals
, ortoString
methods declared injava.lang.Object
on a proxy instance will be encoded anddispatched to the invocation handler'sinvoke
method inthe same manner as interface method invocations are encoded anddispatched, as described above. The declaring class of theMethod
object passed toinvoke
will bejava.lang.Object
. Other public methods of a proxyinstance inherited fromjava.lang.Object
are notoverridden by a proxy class, so invocations of those methods behavelike they do for instances ofjava.lang.Object
.Methods Duplicated in Multiple Proxy Interfaces
When two or more interfaces of a proxy class contain a method withthe same name and parameter signature, the order of the proxy class'sinterfaces becomes significant. When such a duplicate methodis invoked on a proxy instance, the
Method
object passedto the invocation handler will not necessarily be the one whosedeclaring class is assignable from the reference type of the interfacethat the proxy's method was invoked through. This limitation existsbecause the corresponding method implementation in the generated proxyclass cannot determine which interface it was invoked through.Therefore, when a duplicate method is invoked on a proxy instance, theMethod
object for the method in the foremost interfacethat contains the method (either directly or inherited through asuperinterface) in the proxy class's list of interfaces is passed tothe invocation handler'sinvoke
method, regardless of thereference type through which the method invocation occurred.If a proxy interface contains a method with the same name andparameter signature as the
hashCode
,equals
,ortoString
methods ofjava.lang.Object
,when such a method is invoked on a proxy instance, theMethod
object passed to the invocation handler will havejava.lang.Object
as its declaring class. In other words,the public, non-final methods ofjava.lang.Object
logically precede all of the proxy interfaces for the determination ofwhichMethod
object to pass to the invocation handler.Note also that when a duplicate method is dispatched to aninvocation handler, the
invoke
method may only throwchecked exception types that are assignable to one of the exceptiontypes in thethrows
clause of the method in all ofthe proxy interfaces that it can be invoked through. If theinvoke
method throws a checked exception that is notassignable to any of the exception types declared by the method in oneof the the proxy interfaces that it can be invoked through, then anuncheckedUndeclaredThrowableException
will be thrown bythe invocation on the proxy instance. This restriction means that notall of the exception types returned by invokinggetExceptionTypes
on theMethod
objectpassed to theinvoke
method can necessarily be thrownsuccessfully by theinvoke
method.
Serialization
Since
java.lang.reflect.Proxy
implementsjava.io.Serializable
, proxy instances can be serialized,as described in this section. If a proxy instance contains aninvocation handler that is not assignable tojava.io.Serializable
, however, then ajava.io.NotSerializableException
will be thrown if suchan instance is written to ajava.io.ObjectOutputStream
.Note that for proxy classes, implementingjava.io.Externalizable
has the same effect with respectto serialization as implementingjava.io.Serializable
:thewriteExternal
andreadExternal
methodsof theExternalizable
interface will never be invoked ona proxy instance (or an invocation handler) as part of itsserialization process. As with allClass
objects, theClass
object for a proxy class is always serializable.A proxy class has no serializable fields and a
serialVersionUID
of0L
. In other words,when theClass
object for a proxy class is passed to thestaticlookup
method ofjava.io.ObjectStreamClass
, the returnedObjectStreamClass
instance will have the followingproperties:
- Invoking its
getSerialVersionUID
method will return0L
.- Invoking its
getFields
method will return an arrayof length zero.- Invoking its
getField
method with anyString
argument will returnnull
.The stream protocol for Object Serialization supports a type codenamed
TC_PROXYCLASSDESC
, which is a terminal symbol inthe grammar for the stream format; its type and value are defined bythe following constant field in thejava.io.ObjectStreamConstants
interface:final static byte TC_PROXYCLASSDESC = (byte)0x7D;The grammar also includes the following two rules, the first beingan alternate expansion of the original newClassDesc rule:
newClassDesc:
TC_PROXYCLASSDESC
newHandle proxyClassDescInfoproxyClassDescInfo:
(int)<count>
proxyInterfaceName[count] classAnnotation superClassDescproxyInterfaceName:
(utf)
When an
ObjectOutputStream
serializes the classdescriptor for a class that is a proxy class, as determined by passingitsClass
object to theProxy.isProxyClass
method, it uses theTC_PROXYCLASSDESC
type code insteadofTC_CLASSDESC
, following the rules above. In theexpansion of proxyClassDescInfo, the sequence ofproxyInterfaceName items are the names of all of the interfacesimplemented by the proxy class, in the order that they are returned byinvoking thegetInterfaces
method on theClass
object. The classAnnotation andsuperClassDesc items have the same meaning as they do in theclassDescInfo rule. For a proxy class, superClassDescis the class descriptor for its superclass,java.lang.reflect.Proxy
; including this descriptor allowsfor the evolution of the serialized representation of the classProxy
for proxy instances.For non-proxy classes,
ObjectOutputStream
calls itsprotectedannotateClass
method to allow subclasses towrite custom data to the stream for a particular class. For proxyclasses, instead ofannotateClass
, the following methodinjava.io.ObjectOutputStream
is called with theClass
object for the proxy class:protected void annotateProxyClass(Class cl) throws IOException;The default implementation of
annotateProxyClass
inObjectOutputStream
does nothing.When an
ObjectInputStream
encounters the type codeTC_PROXYCLASSDESC
, it deserializes the class descriptorfor a proxy class from the stream, formatted as described above.Instead of calling itsresolveClass
method to resolve theClass
object for the class descriptor, the followingmethod injava.io.ObjectInputStream
is called:protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException;The list of interface names that were deserialized in the proxyclass descriptor are passed as the
interfaces
argument toresolveProxyClass
.The default implementation of
resolveProxyClass
inObjectInputStream
returns the results of callingProxy.getProxyClass
with the list ofClass
objects for the interfaces named in theinterfaces
parameter. TheClass
object used for each interface namei
is the value retuned by callingClass.forName(i, false, loader)whereloader
is the first non-null class loader up theexecution stack, ornull
if no non-null class loaders areon the stack. This is the same class loader choice made by thedefault behavior of theresolveClass
method. This samevalue ofloader
is also the class loader passed toProxy.getProxyClass
. IfProxy.getProxyClass
throws anIllegalArgumentException
,resolveClass
will throw aClassNotFoundException
containing theIllegalArgumentException
.Since a proxy class never has its own serializable fields, theclassdata[] in the stream representation of a proxy instanceconsists wholly of the instance data for its superclass,
java.lang.reflect.Proxy
.Proxy
has oneserializable field,h
, which contains the invocationhandler for the proxy instance.
Examples
Here is a simple example that prints out a message before and aftera method invocation on an object that implements an arbitrary list ofinterfaces:
public interface Foo { Object bar(Object obj) throws BazException; } public class FooImpl implements Foo { Object bar(Object obj) throws BazException { // ... } } public class DebugProxy implements java.lang.reflect.InvocationHandler { private Object obj; public static Object newInstance(Object obj) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new DebugProxy(obj)); } private DebugProxy(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Object result; try { System.out.println("before method " + m.getName()); result = m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("after method " + m.getName()); } return result; } }To construct a
DebugProxy
for an implementation of theFoo
interface and call one of its methods:Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); foo.bar(null);Here is an example of a utility invocation handler class thatprovides default proxy behavior for methods inherited from
java.lang.Object
and implements delegation of certainproxy method invocations to distinct objects depending on theinterface of the invoked method:import java.lang.reflect.*; public class Delegator implements InvocationHandler { // preloaded Method objects for the methods in java.lang.Object private static Method hashCodeMethod; private static Method equalsMethod; private static Method toStringMethod; static { try { hashCodeMethod = Object.class.getMethod("hashCode", null); equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class }); toStringMethod = Object.class.getMethod("toString", null); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } private Class[] interfaces; private Object[] delegates; public Delegator(Class[] interfaces, Object[] delegates) { this.interfaces = (Class[]) interfaces.clone(); this.delegates = (Object[]) delegates.clone(); } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Class declaringClass = m.getDeclaringClass(); if (declaringClass == Object.class) { if (m.equals(hashCodeMethod)) { return proxyHashCode(proxy); } else if (m.equals(equalsMethod)) { return proxyEquals(proxy, args[0]); } else if (m.equals(toStringMethod)) { return proxyToString(proxy); } else { throw new InternalError( "unexpected Object method dispatched: " + m); } } else { for (int i = 0; i < interfaces.length; i++) { if (declaringClass.isAssignableFrom(interfaces[i])) { try { return m.invoke(delegates[i], args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } return invokeNotDelegated(proxy, m, args); } } protected Object invokeNotDelegated(Object proxy, Method m, Object[] args) throws Throwable { throw new InternalError("unexpected method dispatched: " + m); } protected Integer proxyHashCode(Object proxy) { return new Integer(System.identityHashCode(proxy)); } protected Boolean proxyEquals(Object proxy, Object other) { return (proxy == other ? Boolean.TRUE : Boolean.FALSE); } protected String proxyToString(Object proxy) { return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); } }Subclasses of
Delegator
can overrideinvokeNotDelegated
to implement the behavior of proxymethod invocations not to be directly delegated to other objects, andthey can overrideproxyHashCode
,proxyEquals
, andproxyToString
to overridethe default behavior of the methods the proxy inherits fromjava.lang.Object
.To construct a
Delegator
for an implementation of theFoo
interface:Class[] proxyInterfaces = new Class[] { Foo.class }; Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces, new Delegator(proxyInterfaces, new Object[] { new FooImpl() }));Note that the implementation of the
Delegator
classgiven above is intended to be more illustrative than optimized; forexample, instead of caching and comparing theMethod
objects for thehashCode
,equals
, andtoString
methods, it could just match them by theirstring names, because none of those method names are overloaded injava.lang.Object
.