Prefácio
O "agente dinâmico" realmente se origina do modo proxy no modo de design, e o modo proxy usa o objeto proxy para preencher as solicitações do usuário e bloquear o acesso dos usuários a objetos reais.
Para dar o exemplo mais simples, queremos "FQ" para acessar sites estrangeiros, porque não temos todos os IPs estrangeiros, você pode enviar sua solicitação de datagrama para os hosts estrangeiros que não estão bloqueados e, em seguida, você encaminha a solicitação ao destino, configurando o host estrangeiro e encaminhando -o de volta ao nosso host doméstico após receber a mensagem de resposta.
Neste exemplo, um host estrangeiro é um objeto de proxy, e os hosts que são descartados pela parede são objetos reais. Não podemos acessar diretamente os objetos reais, mas podemos acessá -los indiretamente através de um proxy.
Uma vantagem do modo proxy é que todas as solicitações externas passam pelo objeto Proxy, e o objeto proxy tem o direito de controlar se você tem permissão para acessar realmente o objeto real. Se houver uma solicitação ilegal, o objeto proxy pode rejeitá -lo completamente sem realmente problemas com o objeto real.
Uma das aplicações mais típicas do modo proxy é a estrutura de mola. A AOP da Spring usa a programação orientada a aspectos para isolar a lógica de negócios real de exceções relacionadas ao log e outras informações. Toda vez que você solicita a lógica de negócios, ela corresponde a um objeto proxy. Além de executar as verificações de permissão necessárias e a impressão de logs, esse objeto proxy é o bloco de processamento de lógica de negócios real.
Proxy estático
Existem dois implementadores principais do modelo de proxy, "Proxy estático" e "proxy dinâmico". A diferença essencial entre esses dois é que a classe de proxy anterior requer codificação manual por programadores, enquanto a última classe de proxy é gerada automaticamente. Então, é por isso que você mal ouviu falar do conceito de "proxy estático". Obviamente, é naturalmente mais fácil entender "proxy dinâmico".
Uma coisa que você precisa ficar claro é que o objeto proxy proxies todos os métodos do objeto real, ou seja, o objeto proxy precisa fornecer pelo menos o mesmo nome de método que o objeto real para chamada, para que um objeto proxy precise definir todos os métodos de propriedade do objeto real, incluindo métodos na classe pai.
Vejamos um exemplo de proxy estático simples:
Para ilustrar o problema, definimos uma interface do ISERVICE e deixamos nossa classe real herdar e implementar a interface, para que haja dois métodos em nossa classe real.
Então, como uma classe proxy deve ser definida para concluir o proxy para objetos reais?
De um modo geral, a essência de uma classe de proxy é definir todos os métodos da classe real e adicionar outras operações dentro do método e, finalmente, chamar o método da classe real.
A classe de proxy precisa proxy todos os métodos na classe real, ou seja, métodos exatamente iguais aos métodos na classe real, e o método da classe real será indiretamente chamado dentro desses métodos.
Então, de um modo geral, a classe de proxy optará por herdar diretamente todas as interfaces e classes pais da classe real, a fim de obter todos os métodos dos pais assinaturas da classe real, ou seja, para primeiro os métodos proximais de todos os pais.
Em seguida, o proxy não é um método pai na classe real. No exemplo aqui, o método doservice é o método da própria classe real. Nossa classe de proxy também precisa definir um método com a mesma assinatura de método para proxy.
Dessa forma, mesmo que nossa classe de proxy seja concluída, todos os métodos na classe real podem ser proxyed através da classe de proxy no futuro. Assim:
public static void main (string [] args) {realclass realclass = new realclass (); Proxyclass proxyclass = new proxyclass (realclass); proxyclass.sayhello (); proxyclass.doservice ();}O proxyclass, como um objeto de classe de proxy, pode proxy todos os métodos na classe real e imprimir algumas informações "insignificantes" antes que esses métodos sejam executados.
Essa é basicamente a idéia básica de implementação do modelo de proxy, mas a diferença entre proxy dinâmico e esse tipo de proxy estática é que o proxy dinâmico não requer nossas definições de método uma a uma, e a máquina virtual gerará automaticamente esses métodos para você.
Mecanismo de proxy dinâmico JDK
O que distingue proxy dinâmico do proxy estático é que a classe de proxy de proxy dinâmica é criada dinamicamente pela máquina virtual em tempo de execução e limpa quando a máquina virtual é desinstalada.
Reutilizamos as classes usadas no proxy estático acima para ver como o proxy dinâmico do JDK pode próximo de todos os métodos de uma instância de uma determinada classe.
Defina uma classe de processamento de manipuladores:
A API dinâmica de proxy na função principal chama o JDK para gerar uma instância da classe proxy:
Ainda há bastante código envolvido, vamos analisá -lo pouco a pouco. Primeiro, o RealClass implementa a interface do ISERVICE como nossa classe de proxy e define seu próprio método doService internamente.
Em seguida, definimos uma classe de processamento que herda o InvocationHandler da interface e implementa seu método de invocado declarado exclusivo. Além disso, precisamos declarar um campo membro para armazenar objetos reais, ou seja, objetos de proxy, porque qualquer método que proxy é basicamente baseado em métodos relevantes de objetos reais.
Em relação ao papel desse método de invocar e à importância de vários parâmetros formais, realizaremos uma análise detalhada quando refletirmos o código -fonte do proxy.
Por fim, defina nossa classe de processamento e basicamente execute proxy dinâmico com base no JDK. O método principal é o método NewProxyInstance da classe de proxy. Este método possui três parâmetros. Um é um carregador de classe, o segundo é uma coleção de todas as interfaces implementadas pela classe Proxy e a terceira é a nossa classe de processador personalizada.
A máquina virtual usa o carregador de classe que você fornece em tempo de execução, carrega todas as classes de interface especificadas na área do método e reflete e lê os métodos nessas interfaces e combina a classe do processador para gerar um tipo de proxy.
A última frase pode ser um pouco abstrata. Como "combinar com classes de processador para gerar um tipo de proxy"? Nesse sentido, especificamos os parâmetros de inicialização da máquina virtual e deixamos salvar o arquivo de classe gerado da classe proxy.
-Dsun.misc.proxygenerator.saveGeneratedFiles = true
Descompilarmos esse arquivo de classe através de ferramentas de terceiros e há muito conteúdo. Nós dividimos a análise:
Primeiro de tudo, o nome desta classe de proxy é muito aleatório. Se houver várias classes de proxy a serem geradas em um programa, "$ proxy + número" é o nome da classe.
Em seguida, você notará que essa classe de proxy herda a classe de proxy e a interface ISERVICE que especificamos (anteriormente se várias interfaces fossem especificadas, várias interfaces seriam herdadas aqui).
Em seguida, você descobrirá que esse construtor exige um parâmetro de tipo de invasão e o corpo do construtor é passar nesta instância da InvocationHandler para o campo correspondente do proxy da classe pai para armazenamento. Essa também é uma das razões pelas quais todas as classes de proxy devem usar o proxy como a classe pai, que é divulgar o campo InvocationHandler na classe pai. Mais tarde, saberemos que esse pequeno design levará a uma desvantagem fatal do proxy dinâmico baseado em JDK, que será introduzido posteriormente.
Esse conteúdo também é uma parte relativamente importante da classe proxy. Ele será executado quando a máquina virtual inicializar estaticamente a classe Proxy. Essa grande parte do código completa a função de refletir todos os métodos na interface, e todos os métodos refletidos são armazenados correspondentes a um campo do tipo de método.
Além disso, a máquina virtual também reflete três métodos comuns no objeto, ou seja, a classe proxy também proxy o objeto real herdado do objeto.
Na última parte, o que vemos é que a máquina virtual reflete todos os métodos a serem proxyed com base no bloco de código de inicialização estática e gera métodos de procuração para eles.
Esses métodos parecem muito código, mas na verdade eles são apenas uma linha de código, que retira a classe do processador armazenada durante a instanciação do proxy da classe pai e chama seu método de invocar.
Os parâmetros do método são basicamente os mesmos. O primeiro parâmetro é a instância atual da classe proxy (prova que é inútil passar esse parâmetro no passado), o segundo parâmetro é a instância do método do método e o terceiro parâmetro é o conjunto de parâmetros formais do método. Caso contrário, é nulo.
Agora, vamos dar uma olhada na classe de processador personalizada:
Todos os métodos de classe de proxy chamarão o método Invoke da classe do processador e passarão no método atual da classe proxy. Esse método de invasão pode optar por fazer o método ser chamado normalmente ou pular a chamada do método e até fazer algumas coisas extras antes e depois que o método é realmente chamado.
Essa é a idéia principal do proxy dinâmico do JDK. Vamos resumir brevemente todo o processo de chamada.
Primeiro de tudo, a definição de classe de processador é essencial e deve estar associada a um objeto real, ou seja, a instância da classe proxy.
Em seguida, chamamos qualquer método da classe proxy de fora e, do código -fonte descompilado, sabemos que o método da classe proxy chamará o método de invocar o processador e passará a assinatura do método e o conjunto de parâmetros formais.
Finalmente, se o método pode ser chamado normalmente depende se o corpo do processador invocar o corpo do método realmente chama o método do método.
De fato, o proxy dinâmico implementado com base no JDK é falho, e esses defeitos não são fáceis de corrigir, portanto o CGLIB é popular.
Alguns defeitos e deficiências
Mecanismo de proxy único
Não sei se você percebeu que os exemplos acima não estão disponíveis. A classe de proxy gerada pela máquina virtual herda a classe de proxy para armazenar publicamente suas próprias instâncias de classe de processador. O que isso significa?
A herança de raiz única de Java diz que a classe de proxy não pode mais herdar nenhuma outra classe; portanto, os métodos da classe pai da classe de procuração naturalmente não poderão obter, ou seja, a classe Proxy não pode procurar nenhum método da classe pai na classe real.
Além disso, há outro pequeno detalhe. Gostaria de saber se você notou. Eu escrevi assim.
O método Sayhello aqui está a interface ISERVICE implementada, enquanto o método doservice é um método que pertence ao próprio RealClass. Mas não vemos esse método da classe Proxy, o que significa que esse método não é proxyed.
Portanto, o mecanismo de proxy dinâmico do JDK é único e só pode ser métodos proxy na coleta de interface da classe proxy.
Valor de retorno hostil
Observe que o NewProxyInstance retorna uma instância da classe proxy "$ proxy0", mas é retornada como o tipo de objeto e você não pode forçar a instância do objeto ao tipo "$ proxy0".
Embora saibamos que essa instância do objeto é realmente do tipo "$ proxy0", o tipo "$ proxy0" não existe durante o período de compilação, e o compilador naturalmente não permitirá que você o force a um tipo inexistente. Portanto, geralmente forçará apenas uma das interfaces implementadas por esta classe proxy.
realclass rc = new realclass (); myhanlder hanlder = new Myhanlder (rc); iservice obj = (iservice) proxy.newproxyinstance (rc.getclass (). getclassloader (), nova classe [] {iservice.class}, hanlder);Programa de execução de saída:
Procurador Começando ...... Olá, mundo ... Terminamento de proxy ......
Então a pergunta vem novamente. Se a nossa classe de proxy implementar várias interfaces, a qual tipo de interface você deve forçá -lo? Agora, assumindo que a classe proxy implementa as interfaces A e B, se a última instância for forçada a A, você naturalmente não poderá chamar todos os métodos na interface B implementados pela classe Proxy e vice -versa.
Isso leva diretamente a um resultado. Você precisa saber qual método está em qual interface. Se você o forçar para a interface correspondente antes de chamar um método, ela é bastante amigável.
O exposto acima é o que achamos que não é elegante no mecanismo dinâmico de proxy baseado no JDK. Obviamente, suas vantagens são definitivamente maiores que essas desvantagens. No próximo artigo, apresentaremos uma biblioteca de procuração dinâmica do CGLIB amplamente usada por várias estruturas. Sua camada subjacente é baseada na estrutura de operação ByteCode ASM e não depende mais da herança para implementá -la, resolvendo perfeitamente as deficiências do proxy único do JDK.
Todos os códigos, imagens e arquivos no artigo são armazenados na nuvem no meu github:
(https://github.com/singleyam/overview_java)
Você também pode optar por baixar localmente.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.