Movatterモバイル変換


[0]ホーム

URL:


Skip toContent

Dynamic Proxy Classes

Contents

Introduction
Dynamic Proxy API
Serialization
Examples

Introduction

Adynamic proxy class is a class that implements a listof interfaces specified at runtime such that a method invocationthrough one of the interfaces on an instance of the class will beencoded and dispatched to another object through a uniforminterface. Thus, a dynamic proxy class can be used to create atype-safe proxy object for a list of interfaces without requiringpre-generation of the proxy class, such as with compile-time tools.Method invocations on an instance of a dynamic proxy class aredispatched to a single method in the instance'sinvocationhandler, and they are encoded with ajava.lang.reflect.Method object identifying the methodthat was invoked and an array of typeObjectcontaining the arguments.

Dynamic proxy classes are useful to an application or librarythat needs to provide type-safe reflective dispatch of invocationson objects that present interface APIs. For example, an applicationcan use a dynamic proxy class to create an object that implementsmultiple arbitrary event listener interfaces-- interfaces thatextendjava.util.EventListener-- to process a varietyof events of different types in a uniform fashion, such as bylogging all such events to a file.

Dynamic Proxy Class API

Adynamic proxy class (simply referred to as aproxyclass below) is a class that implements a list of interfacesspecified at runtime when the class is created.

Aproxy interface is such an interface that isimplemented by a proxy class.

Aproxy instance is an instance of aproxyclass.

Creating a Proxy Class

Proxy classes, as well as instances of them, are created usingthe static methods of the classjava.lang.reflect.Proxy.

TheProxy.getProxyClass method returns thejava.lang.Class object for a proxy class given a classloader and an array of interfaces. The proxy class will be definedin the specified class loader and will implement all of thesupplied interfaces. If a proxy class for the same permutation ofinterfaces has already been defined in the class loader, then theexisting proxy class will be returned; otherwise, a proxy class forthose interfaces will be generated dynamically and defined in theclass loader.

There are several restrictions on the parameters that may bepassed toProxy.getProxyClass:

If any of these restrictions are violated,Proxy.getProxyClass will throw anIllegalArgumentException. If theinterfaces array argument or any of its elements arenull, aNullPointerException will bethrown.

Note that the order of the specified proxy interfaces issignificant: two requests for a proxy class with the samecombination of interfaces but in a different order will result intwo distinct proxy classes. Proxy classes are distinguished by theorder of their proxy interfaces in order to provide deterministicmethod invocation encoding in cases where two or more of the proxyinterfaces share a method with the same name and parametersignature; this reasoning is described in more detail in thesection below titledMethods Duplicated inMultiple Proxy Interfaces.

So that a new proxy class does not need to be generated eachtimeProxy.getProxyClass is invoked with the sameclass loader and list of interfaces, the implementation of thedynamic proxy class API should keep a cache of generated proxyclasses, keyed by their corresponding loaders and interface list.The implementation should be careful not to refer to the classloaders, interfaces, and proxy classes in such a way as to preventclass loaders, and all of their classes, from being garbagecollected when appropriate.

Proxy Class Properties

A proxy class has the following properties:

Creating a Proxy Instance

Each proxy class has one public constructor that takes oneargument, an implementation of the interfaceInvocationHandler.

Each proxy instance has an associated invocation handler object,the one that was passed to its constructor. Rather than having touse the reflection API to access the public constructor, a proxyinstance can be also be created by calling theProxy.newProxyInstance method, which combines theactions of callingProxy.getProxyClass with invokingthe constructor with an invocation handler.Proxy.newProxyInstance throwsIllegalArgumentException for the same reasons thatProxy.getProxyClass does.

Proxy Instance Properties

A proxy instance has the following properties:

Methods Duplicated in MultipleProxy Interfaces

When two or more interfaces of a proxy class contain a methodwith the same name and parameter signature, the order of the proxyclass's interfaces becomes significant. When such aduplicatemethod is invoked on a proxy instance, theMethodobject passed to the invocation handler will not necessarily be theone whose declaring class is assignable from the reference type ofthe interface that the proxy's method was invoked through. Thislimitation exists because the corresponding method implementationin the generated proxy class cannot determine which interface itwas invoked through. Therefore, when a duplicate method is invokedon a proxy instance, theMethod object for the methodin the foremost interface that contains the method (either directlyor inherited through a superinterface) in the proxy class's list ofinterfaces is passed to the invocation handler'sinvoke method, regardless of the reference typethrough which the method invocation occurred.

If a proxy interface contains a method with the same name andparameter signature as thehashCode,equals, ortoString methods ofjava.lang.Object, when such a method is invoked on aproxy instance, theMethod object passed to theinvocation handler will havejava.lang.Object as itsdeclaring class. In other words, the public, non-final methods ofjava.lang.Object logically precede all of the proxyinterfaces for the determination of whichMethodobject to pass to the invocation handler.

Note also that when a duplicate method is dispatched to aninvocation handler, theinvoke method may only throwchecked exception types that are assignable to one of the exceptiontypes in thethrows clause of the method inallof the 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 inone of the proxy interfaces that it can be invoked through, then anuncheckedUndeclaredThrowableException will be thrownby the invocation on the proxy instance. This restriction meansthat not all of the exception types returned by invokinggetExceptionTypes on theMethod objectpassed to theinvoke method can necessarily be thrownsuccessfully by theinvoke method.

Serialization

Sincejava.lang.reflect.Proxy implementsjava.io.Serializable, proxy instances can beserialized, as described in this section. If a proxy instancecontains an invocation handler that is not assignable tojava.io.Serializable, however, then ajava.io.NotSerializableException will be thrown ifsuch an instance is written to ajava.io.ObjectOutputStream. Note that for proxyclasses, implementingjava.io.Externalizable has thesame effect with respect to serialization as implementingjava.io.Serializable: thewriteExternalandreadExternal methods of theExternalizable interface will never be invoked on aproxy instance (or an invocation handler) as part of itsserialization process. As with allClass objects, theClass object for a proxy class is alwaysserializable.

A proxy class has no serializable fields and aserialVersionUID of0L. In other words,when theClass object for a proxy class is passed tothe staticlookup method ofjava.io.ObjectStreamClass, the returnedObjectStreamClass instance will have the followingproperties:

The stream protocol for Object Serialization supports a typecode namedTC_PROXYCLASSDESC, which is a terminalsymbol in the grammar for the stream format; its type and value aredefined by the following constant field in thejava.io.ObjectStreamConstants interface:

    final static byte TC_PROXYCLASSDESC = (byte)0x7D;

The grammar also includes the following two rules, the firstbeing an alternate expansion of the originalnewClassDescrule:

newClassDesc:
        TC_PROXYCLASSDESCnewHandleproxyClassDescInfo

proxyClassDescInfo:
        (int)<count>proxyInterfaceName[count]classAnnotationsuperClassDesc

proxyInterfaceName:
        (utf)

When anObjectOutputStream serializes the classdescriptor for a class that is a proxy class, as determined bypassing itsClass object to theProxy.isProxyClass method, it uses theTC_PROXYCLASSDESC type code instead ofTC_CLASSDESC, following the rules above. In theexpansion ofproxyClassDescInfo, the sequence ofproxyInterfaceName items are the names of all of theinterfaces implemented by the proxy class, in the order that theyare returned by invoking thegetInterfaces method ontheClass object. TheclassAnnotation 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 descriptorallows for the evolution of the serialized representation of theclassProxy 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 followingmethod injava.io.ObjectOutputStream is called withtheClass object for the proxy class:

    protected void annotateProxyClass(Class cl) throws IOException;

The default implementation ofannotateProxyClass inObjectOutputStream does nothing.

When anObjectInputStream encounters the type codeTC_PROXYCLASSDESC, it deserializes the classdescriptor for a proxy class from the stream, formatted asdescribed above. Instead of calling itsresolveClassmethod to resolve theClass object for the classdescriptor, the following method 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 theinterfaces argumenttoresolveProxyClass.

The default implementation ofresolveProxyClass inObjectInputStream returns the results of callingProxy.getProxyClass with the list ofClass objects for the interfaces named in theinterfaces parameter. TheClass objectused for each interface namei is the value retuned bycalling

        Class.forName(i, false, loader)
whereloader is the first non-null class loader up theexecution stack, ornull if no non-null class loadersare on 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,resolveClasswill 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 andafter a method invocation on an object that implements an arbitrarylist 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 aDebugProxy for an implementation oftheFoo 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 fromjava.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 ofDelegator can overrideinvokeNotDelegated to implement the behavior of proxymethod invocations not to be directly delegated to other objects,and they can overrideproxyHashCode,proxyEquals, andproxyToString tooverride the default behavior of the methods the proxy inheritsfromjava.lang.Object.

To construct aDelegator for an implementation oftheFoo 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 theDelegator classgiven above is intended to be more illustrative than optimized; forexample, instead of caching and comparing theMethodobjects for thehashCode,equals, andtoString methods, it could just match them by theirstring names, because none of those method names are overloaded injava.lang.Object.


Copyright © 1993, 2025, Oracleand/or its affiliates. All rights reserved.
Contact Us

[8]ページ先頭

©2009-2025 Movatter.jp