Conteúdo
Introdução
Dynamic Proxy API
Serialização
Exemplos
Introdução
Uma dinâmica de proxy de classe é uma classe que implementa uma lista de interfaces especificadas em tempo de execução, de tal forma que um método invocationthrough uma das interfaces em uma instância da classe será beencoded e expedidos para outro objeto através de uma uniforminterface. Assim, uma classe proxy dinâmica pode ser usada para criar um objeto proxy atype-safe para uma lista de interfaces sem requiringpre-generation da classe proxy, como com ferramentas de tempo de compilação.As invocações de método em uma instância de uma classe proxy dinâmica são incompatíveis com um único método no invocationhandler da instância e são codificadas com um objetojava.lang.reflect.Method
identificando o methodthat foi invocado e uma matriz do tipo Object
contendo os argumentos.
as classes de proxy dinâmico são úteis para um aplicativo ou biblioteca que precisa fornecer despacho reflexivo tipo-seguro de objetos invocationson que apresentam APIs de interface. Por exemplo, um applicationcan usar um proxy dinâmico classe para criar um objeto que implementsmultiple arbitrário de ouvinte de evento interfaces– interfaces thatextend java.util.EventListener
— para processar um varietyof eventos de diferentes tipos de uma forma uniforme, como bylogging todos esses eventos para um arquivo.
API de classe de Proxy dinâmico
uma classe de proxy dinâmico (simplesmente referida como um proxyclass abaixo) é uma classe que implementa uma lista de interfacespecificado em tempo de execução quando a classe é criada.
uma interface proxy é uma interface que éimplementada por uma classe proxy.
uma instância proxy é uma instância de um proxyclass.
criando uma classe Proxy
classes Proxy, bem como instâncias deles, são criados usandoOs métodos estáticos da classe java.idioma.reflectir.Procuracao.
o método Proxy.getProxyClass
retorna o objeto java.lang.Class
para uma classe proxy dada um classloader e uma matriz de interfaces. A classe proxy será definida no carregador de classe especificado e implementará todas as interfaces fornecidas. Se uma classe proxy para a mesma permutação deinterfaces já tiver sido definida no carregador de classes, então a classe proxy existente será retornada; caso contrário, uma classe proxy para essas interfaces serão geradas dinamicamente e definidas no carregador de classe.
Existem várias restrições sobre os parâmetros que podem bepassed para Proxy.getProxyClass
:
- Todos os
Class
objetos eminterfaces
array deve representar interfaces, notclasses ou tipos primitivos. - não há dois elementos na matriz
interfaces
podem referir-se a objetos idênticosClass
. - todos os tipos de interface devem ser visíveis pelo nome através do carregador de classe especificado. Em outras palavras, para a classe loader
cl
e cada interfacei
, o followingexpression deve ser verdade:Class.forName(i.getName(), false, cl) == i
- Todos os não-interfaces públicas devem estar no mesmo pacote;caso contrário, não seria possível para a classe de proxy toimplement todas as interfaces, independentemente de qual pacote ele isdefined em.
- para qualquer conjunto de métodos membros das interfaces especificadas que tenham a mesma assinatura:
- se o tipo de retorno de qualquer um dos métodos for um tipo primitivo ouvoid, todos os métodos devem ter o mesmo tipo de retorno.
- caso contrário, um dos métodos deve ter um tipo de retorno que seja atribuível a todos os tipos de retorno do restante dos métodos.
- a classe proxy resultante não deve exceder nenhum limite imposto aclasses pela máquina virtual. Por exemplo, a VM pode limitar o número de interfaces que uma classe pode implementar a 65535; nesse caso, o tamanho da matriz
interfaces
não deve exceder 65535.
se alguma dessas restrições for violada,Proxy.getProxyClass
lançará um IllegalArgumentException
. Se o argumento da matriz interfaces
ou qualquer um de seus elementos fornull
, um NullPointerException
será criado.
observe que a ordem das interfaces de proxy especificadas é significativa: duas solicitações para uma classe de proxy com a mesma combinação de interfaces, mas em uma ordem diferente, resultarão em duas classes de proxy distintas. Classes de Proxy são distinguidos por theorder de seu procurador interfaces para fornecer deterministicmethod invocação de codificação em casos onde dois ou mais dos proxyinterfaces compartilhar um método com o mesmo nome e parametersignature; este raciocínio é descrito em mais detalhes no thesection abaixo intitulada Métodos Duplicados inMultiple Interfaces do Proxy.
Para que uma nova classe de proxy não precisa ser gerado eachtime Proxy.getProxyClass
é invocada com o sameclass carregador e lista de interfaces, a implementação de thedynamic classe de proxy API deve manter um cache dos gerado proxyclasses, codificada pelo seu correspondente carregadores e lista de interface.A implementação deve ter cuidado para não se referir às classes classloaders, interfaces e proxy de forma a impedir que os carregadores de classe e todas as suas classes sejam coletados quando apropriado.
propriedades da classe Proxy
uma classe proxy tem as seguintes propriedades:
- As classes Proxy são públicas, finais e não abstratas.
- o nome não qualificado de uma classe proxy não é especificado. O espaço de nomes de classe que começam com a string
"$Proxy"
deve, no entanto, ser reservado para classes proxy. - uma classe proxy estende
java.lang.reflect.Proxy
. - uma classe proxy implementa exatamente as interfaces especificadas atits criação, na mesma ordem.
- se uma classe proxy implementa uma interface não pública, ela será definida no mesmo pacote que essa interface. Caso contrário, o pacote de uma classe proxy também não é especificado. Observe que o packagesealing não impedirá que uma classe proxy seja definida com sucesso em um pacote específico em tempo de execução, e nem willclasses já definidas no mesmo Carregador de classe e no samepackage com signatários específicos.
- uma vez que uma classe de proxy implementa todas as interfaces specifiedat a sua criação, invocando
getInterfaces
suaClass
objeto irá retornar um array contendo os samelist de interfaces (na ordem especificada na sua criação),invocandogetMethods
suaClass
objectwill retornar uma matriz deMethod
objetos que includeall dos métodos dessas interfaces, e invocandogetMethod
vai encontrar métodos de proxy interfaces aswould ser o esperado. - o método
Proxy.isProxyClass
retornará true ifit é passado uma classe proxy – uma classe retornada porProxy.getProxyClass
ou a classe de um objeto retornado porProxy.newProxyInstance
– e false caso contrário. Thereliability deste método é importante para a capacidade de usá-lopara tomar decisões de segurança, então sua implementação não deve justtest se a classe em questão se estendejava.lang.reflect.Proxy
. - o
java.security.ProtectionDomain
de um proxyclass é o mesmo das classes de Sistema carregadas pelo carregador bootstrapclass, comojava.lang.Object
, porque o código para uma classe proxy é gerado pelo código de sistema confiável. Este domínio de proteção normalmente será concedidojava.security.AllPermission
.
criando uma instância Proxy
cada classe proxy tem um construtor público que leva oneargument, uma implementação da interface InvocationHandler
.
cada instância de proxy tem um objeto manipulador de Invocação associado, aquele que foi passado para seu construtor. Em vez de ter usado a API de reflexão para acessar o construtor público, um proxyinstance também pode ser criado chamando o métodoProxy.newProxyInstance
, que combina as ações de chamar Proxy.getProxyClass
com invocandoo construtor com um manipulador de Invocação.Proxy.newProxyInstance
lançaIllegalArgumentException
pelas mesmas razões que Proxy.getProxyClass
faz.
Proxy Propriedades de Instância
Um proxy exemplo, tem as seguintes propriedades:
- Dado um proxy exemplo
proxy
e um dos theinterfaces implementado pela sua classe de proxyFoo
, thefollowing expressão retornará true:proxy instanceof Foo
e o seguinte elenco de operação será bem sucedido (em vez de throwinga
ClassCastException
):(Foo) proxy
- A estática
Proxy.getInvocationHandler
método willreturn a invocação manipulador associado com o proxy instancepassed como seu argumento. Se o objeto passado paraProxy.getInvocationHandler
não for uma instância de proxy,umIllegalArgumentException
será lançado. - uma invocação de método de interface em uma instância proxy será codificada e enviada para o método
invoke
do manipulador de Invocação, conforme descrito abaixo.a própria instância proxy será passada como o primeiro argumentof
invoke
, que é do tipoObject
.o segundo argumento passado para
invoke
será a instânciajava.lang.reflect.Method
correspondente ao método interface invocado na instância proxy. A classe declarante do objetoMethod
será a interface em que themethod foi declarado, que pode ser uma superinterface do proxyinterface pela qual a classe proxy herda o método.o terceiro argumento passado para
invoke
será anarray de objetos contendo os valores dos argumentos passados EMA invocação do método na instância proxy. Argumentos de primitivetypes são agrupados em uma instância da classe primitivewrapper apropriada, comojava.lang.Integer
oujava.lang.Boolean
. A implementação do métodoinvoke
é livre para modificar o conteúdo do thisarray.o valor retornado pelo método
invoke
se tornará o valor de retorno da invocação do método na instância proxy. Ifthe valor de retorno declarado do método de interface é um primitivetype, então o valor retornado porinvoke
deve ser aninstance da classe wrapper primitiva correspondente; caso contrário, deve ser um tipo atribuível ao tipo de retorno declarado. Se o valor retornado porinvoke
fornull
e o tipo de retorno do método de interface for primitivo, então umNullPointerException
será lançado pelo methodinvocation na instância de proxy. Se o valor retornado porinvoke
não for compatível com o tipo de retorno do method’dseclared conforme descrito acima, umClassCastException
será lançado pelo proxyinstance.se uma exceção for lançada pelo método
invoke
, ela também será lançada pela invocação do método na instância proxy.O tipo de exceção deve ser atribuível a qualquer um dos tipos de exceção declarados na assinatura do interface methodor aos tipos de exceção não verificadosjava.lang.RuntimeException
oujava.lang.Error
. Se uma exceção verificada for lançada porinvoke
que não é atribuível a nenhum dos exceptiontypes declarados na cláusulathrows
do interfacemethod, então umUndeclaredThrowableException
será lançado pela invocação do método na instância proxy. OUndeclaredThrowableException
será construído coma exceção que foi lançada pelo métodoinvoke
. - uma invocação dos métodos
hashCode
,equals
outoString
declarados emjava.lang.Object
em uma instância proxy será codificada e despachada para o métodoinvoke
do manipulador de invocação da mesma maneira que as invocações do método de interface são codificadas e despachadas, conforme descrito acima. A classe de declaração do objetoMethod
passou parainvoke
willbejava.lang.Object
. Outros métodos públicos de uma proxyinstance herdada dejava.lang.Object
não são substituídos por uma classe proxy, portanto, invocações desses métodos se comportam como fazem para instâncias dejava.lang.Object
.
métodos duplicados em Interfaces MultipleProxy
quando duas ou mais interfaces de uma classe proxy contêm um métodocom o mesmo nome e assinatura de parâmetro, a ordem das interfaces do proxyclass se torna significativa. Quando tal duplicatemethod é invocado em uma instância de proxy, o objeto Method
passado para o manipulador de Invocação não será necessariamente aquele cuja classe de declaração é atribuível do tipo de referência da interface pela qual o método do proxy foi invocado. Essa limitação existe porque a implementação do método correspondentena classe proxy gerada não pode determinar por qual interface ele foi invocado. Portanto, quando um método duplicado é invokedon um proxy exemplo, o Method
objeto para o methodin a principal interface que contém o método (ou directlyor herdadas através de uma superinterface) na classe proxy da lista ofinterfaces é passado para a invocação do manipuladorinvoke
método, independentemente da referência typethrough que a chamada do método ocorreu.
Se um proxy de interface contém um método com o mesmo nome andparameter assinatura como o hashCode
,equals
, ou toString
métodos dejava.lang.Object
, quando um método é invocado no aproxy exemplo, o Method
objeto passado para theinvocation processador vai ter java.lang.Object
como itsdeclaring classe. Em outras palavras, os métodos públicos e não finais dejava.lang.Object
precedem logicamente todos os proxyinterfaces para a determinação dos quais Method
se opõem a passar para o manipulador de Invocação.
Observe também que, quando um método duplicado é enviado para aninvocation manipulador, o invoke
método só pode throwchecked tipos de exceção que são atribuíveis a um dos exceptiontypes em throws
cláusula do método em allof o proxy de interfaces que podem ser invocadas através de. Se o métodoinvoke
lançar uma exceção verificada que não é atribuível a nenhum dos tipos de exceção declarados pelo método em uma das interfaces de proxy pelas quais ele pode ser invocado, então anunchecked UndeclaredThrowableException
será lançado pela invocação na instância de proxy. Essa restrição significaque nem todos os tipos de exceção retornados invocandogetExceptionTypes
no Method
objectpassed para o método invoke
podem necessariamente ser lançados com sucesso pelo método invoke
.
serialização
desde java.lang.reflect.Proxy
implementajava.io.Serializable
, as instâncias proxy podem serserializadas, conforme descrito nesta seção. Se um proxy instancecontém um manipulador de invocação que não é atribuível ajava.io.Serializable
, no entanto, umjava.io.NotSerializableException
será lançado ifsuch uma instância é gravada em umjava.io.ObjectOutputStream
. Observe que, para proxyclasses, implementar java.io.Externalizable
tem o mesmo efeito em relação à serialização como implementarjava.io.Serializable
: os métodos writeExternal
e readExternal
da interfaceExternalizable
nunca serão invocados na instância aproxy (ou um manipulador de Invocação) como parte de seu processo de serialização. Como com todos os objetos Class
, o objetoClass
para uma classe proxy é sempresserializable.
uma classe proxy não tem campos serializáveis e umserialVersionUID
de 0L
. Em outras palavras,quando a Class
objeto para uma classe de proxy é passado à estática lookup
métodojava.io.ObjectStreamClass
, a devolvidosObjectStreamClass
instância terá o followingproperties:
- Invocando o seu
getSerialVersionUID
método willreturn0L
. - invocar seu método
getFields
retornará um arrayof length zero. - invocar seu método
getField
com qualquer argumentoString
retornaránull
.
o protocolo stream para serialização de objetos suporta um typecode chamado TC_PROXYCLASSDESC
, que é um símbolo terminal na gramática para o formato stream; o seu tipo e o valor aredefined pelo seguinte campo constante nojava.io.ObjectStreamConstants
interface de:
final static byte TC_PROXYCLASSDESC = (byte)0x7D;
A gramática também inclui as seguintes duas regras, o firstbeing uma alternativa de expansão do original newClassDescrule:
newClassDesc:TC_PROXYCLASSDESC
newHandle proxyClassDescInfo
proxyClassDescInfo:(int)<count>
proxyInterfaceName classAnnotationsuperClassDesc
proxyInterfaceName:(utf)
Quando um ObjectOutputStream
serializa o classdescriptor para uma classe que é uma classe de proxy, como determinado ignorando as suas Class
objetoProxy.isProxyClass
método, ele usa oTC_PROXYCLASSDESC
tipo de código em vez deTC_CLASSDESC
, seguindo as regras acima. Noexpansão de proxyClassDescInfo, a sequência deproxyinterfacename itens são os nomes de todos osinterfaces implementados pela classe proxy, na ordem em que eles são retornados invocando o getInterfaces
método Emo Class
objeto. Os itens classAnnotation esuperclassdesc têm o mesmo significado que na regra theclassDescInfo. Para uma classe proxy, superClassDescis o descritor de classe para sua superclasse,java.lang.reflect.Proxy
; incluindo este descritorallows para a evolução da representação serializada de theclass Proxy
para instâncias proxy.
para classes não-proxy, ObjectOutputStream
chama itsprotected annotateClass
método para permitir subclasses towrite dados personalizados para o fluxo para uma determinada classe. Para proxyclasses, em vez de annotateClass
, o followingmethod em java.io.ObjectOutputStream
é chamado de coma Class
objeto para a classe de proxy:
protected void annotateProxyClass(Class cl) throws IOException;
A implementação padrão do annotateProxyClass
emObjectOutputStream
não faz nada.
quando um ObjectInputStream
encontra o código de tipoTC_PROXYCLASSDESC
, desserializa o classdescriptor para uma classe proxy do fluxo, formatado como descrito acima. Em vez de chamar o seu resolveClass
método para resolver o Class
objeto para o classdescriptor, o método a seguir najava.io.ObjectInputStream
é chamado:
protected Class resolveProxyClass(String interfaces) throws IOException, ClassNotFoundException;
A lista de nomes de interface que foram desserializado em proxyclass descritor de são passados como o interfaces
argumentto resolveProxyClass
.
A implementação padrão do resolveProxyClass
emObjectInputStream
retorna os resultados da chamadaProxy.getProxyClass
com a lista deClass
objetos para as interfaces eminterfaces
parâmetro. O Class
objectused para cada nome de interface i
é o valor reajustado bycalling
Class.forName(i, false, loader)
ondeloader
é o primeiro não-nulo carregador de classe até theexecution pilha, ounull
se não não-null classe loadersare na pilha. Esta é a mesma escolha de carregador de classe feita por thedefault comportamento doresolveClass
método. Este samevalue deloader
também é o carregador de classe passado paraProxy.getProxyClass
. SeProxy.getProxyClass
lançar umIllegalArgumentException
,resolveClass
lançará umClassNotFoundException
contendo oIllegalArgumentException
.
como uma classe proxy nunca tem seus próprios campos serializáveis, theclassdata na representação de fluxo de um proxy instanceconsists totalmente dos dados de instância para sua superclasse,java.lang.reflect.Proxy
. Proxy
tem campo oneserializable, h
, que contém o invocationhandler para a instância proxy.
exemplos
aqui está um exemplo simples que imprime uma mensagem antes e depois de uma invocação de método em um objeto que implementa um arbitrarylist de 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; }}
Para construir um DebugProxy
para uma implementação do Foo
interface e chamar um de seus métodos:
Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); foo.bar(null);
Aqui está um exemplo de um utilitário de invocação de classe de manipulador de thatprovides proxy padrão de comportamento para os métodos herdados dejava.lang.Object
e implementa delegação de certainproxy invocações de método para objetos distintos, dependendo theinterface do chamado método:
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); } 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)) { try { return m.invoke(delegates, 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 de Delegator
pode substituirinvokeNotDelegated
para implementar o comportamento de proxymethod invocações de não ser diretamente delegadas a outros objetos,e eles podem substituir proxyHashCode
,proxyEquals
, e proxyToString
tooverride o comportamento padrão dos métodos proxy inheritsfrom java.lang.Object
.
para construir uma Delegator
para uma implementação da interface Foo
:
Class proxyInterfaces = new Class { Foo.class }; Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces, new Delegator(proxyInterfaces, new Object { new FooImpl() }));
observe que a implementação do classgiven Delegator
acima se destina a ser mais ilustrativa do que otimizada; forexample, em vez de armazenar em cache e comparar os objetos Method
para os métodos hashCode
, equals
etoString
, poderia apenas combiná-los por seus nomes de string, porque nenhum desses nomes de método está sobrecarregado emjava.lang.Object
.