A dynamic proxy class is a class that implements a list
of interfaces specified at runtime such that a method invocation
through one of the interfaces on an instance of the class will be
encoded and dispatched to another object through a uniform
interface. Thus, a dynamic proxy class can be used to create a
type-safe proxy object for a list of interfaces without requiring
pre-generation of the proxy class, such as with compile-time tools.
Method invocations on an instance of a dynamic proxy class are
dispatched to a single method in the instance's invocation
handler, and they are encoded with a
java.lang.reflect.Method
object identifying the method
that was invoked and an array of type Object
containing the arguments.
Dynamic proxy classes are useful to an application or library
that needs to provide type-safe reflective dispatch of invocations
on objects that present interface APIs. For example, an application
can use a dynamic proxy class to create an object that implements
multiple arbitrary event listener interfaces-- interfaces that
extend java.util.EventListener
-- to process a variety
of events of different types in a uniform fashion, such as by
logging all such events to a file.
A dynamic proxy class (simply referred to as a proxy class below) is a class that implements a list of interfaces specified at runtime when the class is created.
A proxy interface is such an interface that is implemented by a proxy class.
A proxy instance is an instance of a proxy class.
Proxy classes, as well as instances of them, are created using the static methods of the class java.lang.reflect.Proxy.
The Proxy.getProxyClass
method returns the
java.lang.Class
object for a proxy class given a class
loader and an array of interfaces. The proxy class will be defined
in the specified class loader and will implement all of the
supplied interfaces. If a proxy class for the same permutation of
interfaces has already been defined in the class loader, then the
existing proxy class will be returned; otherwise, a proxy class for
those interfaces will be generated dynamically and defined in the
class loader.
There are several restrictions on the parameters that may be
passed to Proxy.getProxyClass
:
Class
objects in the
interfaces
array must represent interfaces, not
classes or primitive types.interfaces
array may refer
to identical Class
objects.cl
and every interface i
, the following
expression must be true:
Class.forName(i.getName(), false, cl) == i
interfaces
array must not exceed
65535.If any of these restrictions are violated,
Proxy.getProxyClass
will throw an
IllegalArgumentException
. If the
interfaces
array argument or any of its elements are
null
, a NullPointerException
will be
thrown.
Note that the order of the specified proxy interfaces is significant: two requests for a proxy class with the same combination of interfaces but in a different order will result in two distinct proxy classes. Proxy classes are distinguished by the order of their proxy interfaces in order to provide deterministic method invocation encoding in cases where two or more of the proxy interfaces share a method with the same name and parameter signature; this reasoning is described 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 loader and list of interfaces, the implementation of the
dynamic proxy class API should keep a cache of generated proxy
classes, keyed by their corresponding loaders and interface list.
The implementation should be careful not to refer to the class
loaders, interfaces, and proxy classes in such a way as to prevent
class loaders, and all of their classes, from being garbage
collected when appropriate.
A proxy class has the following properties:
"$Proxy"
is,
however, to be reserved for proxy classes.java.lang.reflect.Proxy
.getInterfaces
on its
Class
object will return an array containing the same
list of interfaces (in the order specified at its creation),
invoking getMethods
on its Class
object
will return an array of Method
objects that include
all of the methods in those interfaces, and invoking
getMethod
will find methods in the proxy interfaces as
would be expected.Proxy.isProxyClass
method will return true if
it is passed a proxy class-- a class returned by
Proxy.getProxyClass
or the class of an object returned
by Proxy.newProxyInstance
-- and false otherwise. The
reliability of this method is important for the ability to use it
to make security decisions, so its implementation should not just
test if the class in question extends
java.lang.reflect.Proxy
.java.security.ProtectionDomain
of a proxy
class is the same as that of system classes loaded by the bootstrap
class loader, such as java.lang.Object
, because the
code for a proxy class is generated by trusted system code. This
protection domain will typically be granted
java.security.AllPermission
.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
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.
Proxy.newProxyInstance
throws
IllegalArgumentException
for the same reasons that
Proxy.getProxyClass
does.
A proxy instance has the following properties:
proxy
and one of the
interfaces implemented by its proxy class Foo
, the
following expression will return true:
proxy instanceof Foo
and the following cast operation will succeed (rather than throwing
a ClassCastException
):
(Foo) proxy
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.invoke
method as described below.
The proxy instance itself will be passed as the first argument
of invoke
, which is of type Object
.
The second argument passed to invoke
will be the
java.lang.reflect.Method
instance corresponding to the
interface method invoked on the proxy instance. The declaring class
of the Method
object will be the interface that the
method was declared in, which may be a superinterface of the proxy
interface that the proxy class inherits the method through.
The third argument passed to invoke
will be an
array of objects containing the values of the arguments passed in
the method invocation on the proxy instance. Arguments of primitive
types are wrapped in an instance of the appropriate primitive
wrapper class, such as java.lang.Integer
or
java.lang.Boolean
. The implementation of the
invoke
method is free to modify the contents of this
array.
The value returned by the invoke
method will become
the return value of the method invocation on the proxy instance. If
the declared return value of the interface method is a primitive
type, then the value returned by invoke
must be an
instance of the corresponding primitive wrapper class; otherwise,
it must be a type assignable to the declared return type. If the
value returned by invoke
is null
and the
interface method's return type is primitive, then a
NullPointerException
will be thrown by the method
invocation on the proxy instance. If the value returned by
invoke
is otherwise not compatible with the method's
declared return type as described above, a
ClassCastException
will be thrown by the proxy
instance.
If an exception is thrown by the invoke
method, it
will be also thrown by the method invocation on the proxy instance.
The exception's type must be assignable to either any of the
exception types declared in the signature of the interface method
or to the unchecked exception types
java.lang.RuntimeException
or
java.lang.Error
. If a checked exception is thrown by
invoke
that is not assignable to any of the exception
types declared in the throws
clause of the interface
method, then an UndeclaredThrowableException
will be thrown by the method invocation on the proxy instance. The
UndeclaredThrowableException
will be constructed with
the exception that was thrown by the invoke
method.
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, as described above. The declaring class of
the Method
object passed to invoke
will
be java.lang.Object
. Other public methods of a proxy
instance inherited from java.lang.Object
are not
overridden by a proxy class, so invocations of those methods behave
like they do for instances of java.lang.Object
.When two or more interfaces of a proxy class contain a method
with the same name and parameter signature, the order of the proxy
class's interfaces becomes significant. When such a duplicate
method is invoked on a proxy instance, the Method
object passed to the invocation handler will 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. 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 superinterface) 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.
If a proxy interface 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. In other words, the public, non-final methods of
java.lang.Object
logically precede all of the proxy
interfaces for the determination of which Method
object to pass to the invocation handler.
Note also that when a duplicate method is dispatched to an
invocation handler, the invoke
method may 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 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
one 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 Method
object
passed to the invoke
method can necessarily be thrown
successfully by the invoke
method.
Since java.lang.reflect.Proxy
implements
java.io.Serializable
, proxy instances can be
serialized, as described in this section. 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
. Note that for proxy
classes, implementing java.io.Externalizable
has the
same effect with respect to serialization as implementing
java.io.Serializable
: 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.
A proxy class has no serializable fields and a
serialVersionUID
of 0L
. In other words,
when the 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
properties:
getSerialVersionUID
method will
return 0L
.getFields
method will return an array
of length zero.getField
method with any
String
argument will return null
.The stream protocol for Object Serialization supports a type
code named TC_PROXYCLASSDESC
, which is a terminal
symbol in the 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;
The grammar also includes the following two rules, the first being an alternate expansion of the original newClassDesc rule:
newClassDesc:
TC_PROXYCLASSDESC
newHandle proxyClassDescInfo
proxyClassDescInfo:
(int)<count>
proxyInterfaceName[count] classAnnotation
superClassDesc
proxyInterfaceName:
(utf)
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
, 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 and
superClassDesc items have the same meaning as they do in the
classDescInfo rule. For a proxy class, superClassDesc
is the class descriptor for its superclass,
java.lang.reflect.Proxy
; including this descriptor
allows for the evolution of the serialized representation of the
class Proxy
for proxy instances.
For non-proxy classes, ObjectOutputStream
calls its
protected annotateClass
method to allow subclasses to
write custom data to the stream for a particular class. For proxy
classes, instead of annotateClass
, the following
method in java.io.ObjectOutputStream
is called with
the Class
object for the proxy class:
protected void annotateProxyClass(Class cl) throws IOException;
The default implementation of annotateProxyClass
in
ObjectOutputStream
does nothing.
When an ObjectInputStream
encounters the type code
TC_PROXYCLASSDESC
, it deserializes the class
descriptor for a proxy class from the stream, formatted as
described above. Instead of calling its resolveClass
method to resolve the Class
object for the class
descriptor, the following method in
java.io.ObjectInputStream
is called:
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 retuned 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. This is the same class loader choice made by the
default behavior of the resolveClass
method. This same
value of loader
is also the class loader passed to
Proxy.getProxyClass
. If
Proxy.getProxyClass
throws an
IllegalArgumentException
, resolveClass
will throw a ClassNotFoundException
containing the
IllegalArgumentException
.
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
. Proxy
has one
serializable field, h
, which contains the invocation
handler for the proxy instance.
Here is a simple example that prints out a message before and after a method invocation on an object that implements an arbitrary list of interfaces:
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
the Foo
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 that
provides default proxy behavior for methods inherited from
java.lang.Object
and implements delegation of certain
proxy method invocations to distinct objects depending on the
interface 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 override
invokeNotDelegated
to implement the behavior of proxy
method invocations not to be directly delegated to other objects,
and they can override proxyHashCode
,
proxyEquals
, and proxyToString
to
override the default behavior of the methods the proxy inherits
from java.lang.Object
.
To construct a Delegator
for an implementation of
the Foo
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
class
given above is intended to be more illustrative than optimized; for
example, instead of caching and comparing the Method
objects for the hashCode
, equals
, and
toString
methods, it could just match them by their
string names, because none of those method names are overloaded in
java.lang.Object
.