Dinâmica de Classes de Proxy

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 Objectcontendo 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ênticos Class.
  • todos os tipos de interface devem ser visíveis pelo nome através do carregador de classe especificado. Em outras palavras, para a classe loadercl e cada interface i, 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),invocando getMethods sua Class objectwill retornar uma matriz de Method 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 por Proxy.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, como java.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 proxy Foo, 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,um IllegalArgumentException será lançado.
  • uma invocação de método de interface em uma instância proxy será codificada e enviada para o métodoinvoke do manipulador de Invocação, conforme descrito abaixo.

    a própria instância proxy será passada como o primeiro argumentof invoke, que é do tipo Object.

    o segundo argumento passado para invoke será a instância java.lang.reflect.Method correspondente ao método interface invocado na instância proxy. A classe declarante do objeto Method 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, como java.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 por invoke 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 por invoke for null 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 por invoke que não é atribuível a nenhum dos exceptiontypes declarados na cláusula throwsdo interfacemethod, então um UndeclaredThrowableException 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étodo invoke.

  • uma invocação dos métodoshashCode, equals outoString declarados em java.lang.Object em uma instância proxy será codificada e despachada para o método invokedo 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 objeto Method passou para invoke willbe java.lang.Object. Outros métodos públicos de uma proxyinstance herdada de java.lang.Objectnão são substituídos por uma classe proxy, portanto, invocações desses métodos se comportam como fazem para instâncias de java.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 Methodpassado 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 Methodse 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 writeExternale 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 willreturn 0L.
  • 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_PROXYCLASSDESCnewHandle 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 resolveClassmé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, ounullse não não-null classe loadersare na pilha. Esta é a mesma escolha de carregador de classe feita por thedefault comportamento doresolveClassmétodo. Este samevalue deloadertambém é o carregador de classe passado paraProxy.getProxyClass. SeProxy.getProxyClasslançar umIllegalArgumentException,resolveClasslançará umClassNotFoundExceptioncontendo 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 Methodpara 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.

Deixe uma resposta

O seu endereço de email não será publicado.