Através da análise dos artigos anteriores, sabemos que a classe de proxy é gerada através da fábrica proxyclassFactory da classe de proxy. Esta classe de fábrica chamará o método GenerateProxyclass () da classe ProxyGenerator para gerar o bytecode da classe Proxy. A classe ProxyGenerator é armazenada no pacote Sun.MISC. Podemos encontrar essa classe através do código fonte do OpenJDK. O conteúdo principal do método estático geradoProxyclass () desta classe é chamar o método de instância geradoClassFile () para gerar arquivos de classe. Vamos dar uma olhada no que é feito dentro do método generateclassfile ().
byte privado [] generateclassFile () {// A primeira etapa é montar todos os métodos em objetos proxymethod // gerar primeiro métodos de proxy, como tostragem, hashcode, equals, etc. addproxymethod (hashcodemethod, object.class); addProxyMethod (EqualsMethod, object.class); addProxyMethod (ToStringMethod, object.class); // transfira cada método de cada interface e gera um objeto proxymethod para ele para (int i = 0; i <interfaces.length; i ++) {métodos [] métodos = interfaces [i] .getMethods (); for (int j = 0; j <métodos. }} // Para métodos de proxy com a mesma assinatura, verifique se o valor de retorno do método é compatível com (list <roxymethod> assinaturas: proxymethods.values ()) {checkrenunttypes (sigmethods); } // Etapa 2, monte todas as informações de campo e informações do método do arquivo de classe a ser gerado, tente {// Adicione o método do construtor métodos.add (generateconstructor ()); // Transfira o método proxy no cache para (List <Proxymethod> SignMethods: proxymethods.values ()) {for (proxymethod pm: signMethods) {// Adicione campos estáticos da classe proxy, por exemplo: método estático privado m1; fields.add (new FieldInfo (pm.methodfieldname, "ljava/lang/refletir/métod;", acc_private | acc_static)); // Adicione métodos proxy dos métodos da classe proxy.add (pm.generatemethod ()); }} // Adicione métodos de inicialização de campo estático Métodos de inicialização.add (generatestaticTicinitializer ()); } catch (ioexception e) {tiro o novo internalError ("Exceção de E/S inesperada"); } // O método de verificação e a coleta de campo não podem ser maiores que 65535 se (métodos.size ()> 65535) {lança nova ilegalArgumentException ("Limite do método excedido"); } if (fields.size ()> 65535) {lança nova ilegalArgumentException ("limite de campo excedido"); } if (fields.size ()> 65535) {lança nova ilegalArgumentException ("limite de campo excedido"); } // Etapa 3, escreva no arquivo da classe final // Verifique se existe o nome totalmente qualificado da classe proxy no pool constante cp.getclass (Dottoslash (ClassName)); // Verifique se há o nome totalmente qualificado da classe de proxy da classe pai no pool constante, e o nome da classe pai é: "Java/lang/refletir/proxy" cp.getclass (superclassName); // Verifique se o nome qualificado completo da interface da classe proxy para (int i = 0; i <interfaces.length; i ++) {cp.getclass (Dottoslash (interfaces [i] .getName ())); } // Em seguida para começar a gravar o arquivo, defina o pool constante para ler apenas cp.SetReadonly (); ByteArrayOutputStream bout = new ByteArrayOutputStream (); DataOutputStream Dout = new DataOutputStream (BUT); tente {// 1. Escreva para o número mágico Dout.WriteInt (0xcafeBabe); // 2. Escreva no número da versão secundária Dout.WriteShort (ClassFile_minor_version); // 3. Escreva no número da versão principal Dout.WriteShort (classfile_major_version); // 4. Escreva para o constante pool cp.write (dout); // 5. Escreva o modificador de acesso DOUT.WRITESHORT (ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // 6. Escreva o índice da classe Dout.WriteShort (cp.getclass (Dottoslash (ClassName))); // 7. Escreva o índice da classe pai, as classes de proxy geradas são herdadas do proxy Dout.WriteShort (cp.getclass (superclassName)); // 8. Valor da contagem de interface de gravação Dout.WriteShort (interfaces.length); // 9. Interface de gravação definida para (int i = 0; i <interfaces.length; i ++) {Dout.WriteShort (cp.getclass (Dottoslash (interfaces [i] .getName ())))); } // 10. Escreva o valor da contagem de campo Dout.WriteShort (Fields.size ()); // 11. Coleção de campo de gravação para (FieldInfo F: campos) {f.Write (Dout); } // 12. Write Method Count Value Dout.WriteShort (Methods.size ()); // 13. Coleção de métodos de gravação para (MethodInfo M: Métodos) {M.Write (Dout); } // 14. Escreva o valor da contagem de propriedades, o arquivo da classe proxy não possui atributos, por isso é 0 Dout.WriteShort (0); } catch (ioexception e) {tiro o novo internalError ("Exceção de E/S inesperada"); } // Converta em matriz binária em saída de retornoVocê pode ver que o método generateclassFile () é dinamicamente emendado de acordo com a estrutura do arquivo de classe. O que é um arquivo de classe? Aqui, primeiro explicaremos que o arquivo java que geralmente escrevemos termina com .Java. Depois de escrevê -lo, compile -o através do compilador e gerar um arquivo .class. Este arquivo .class é um arquivo de classe. A execução dos programas Java depende apenas dos arquivos de classe e não tem nada a ver com os arquivos Java. Este arquivo de classe descreve as informações de uma classe. Quando precisamos usar uma classe, a máquina virtual Java carrega o arquivo de classe dessa classe com antecedência e executa a inicialização e a verificação relacionada. A máquina virtual Java pode garantir que essas tarefas sejam concluídas antes de você usar esta classe. Só precisamos usá -lo com paz de espírito, sem nos preocupar com como a máquina virtual Java o carrega. Obviamente, os arquivos de classe não precisam necessariamente ser compilados compilando arquivos Java. Você pode até escrever arquivos de classe diretamente através de um editor de texto. Aqui, o proxy dinâmico do JDK gera dinamicamente os arquivos de classe através de programas. Vamos voltar ao código acima novamente e ver que a geração do arquivo de classe é dividida principalmente em três etapas:
Etapa 1: Colete todos os métodos de proxy a serem gerados, envolva -os em objetos proxymethod e registre -os na coleção de mapas.
Etapa 2: Colete todas as informações de campo e informações do método a serem geradas para o arquivo de classe.
Etapa 3: Depois de concluir o trabalho acima, comece a montar o arquivo de classe.
Sabemos que a parte central de uma classe são seus campos e métodos. Vamos nos concentrar no segundo passo para ver quais campos e métodos gera para a classe proxy. Na segunda etapa, as quatro coisas a seguir foram feitas em ordem.
1. Gere um construtor de parâmetros para a classe Proxy, passe em uma referência à instância do InvocationHandler e chame o construtor de parâmetros da classe pai.
2. Itera sobre a coleta de mapas de métodos de proxy, gera o domínio estático do tipo método correspondente para cada método proxy e adicione -o à coleção de campos.
3. Itera sobre a coleta de mapas de métodos de proxy, gera o objeto MethodInfo correspondente para cada método proxy e adicione -o à coleta de métodos.
4. Gere um método de inicialização estática para a classe de proxy. Esse método de inicialização estática atribui principalmente a referência de cada método proxy ao campo estático correspondente.
Através da análise acima, podemos saber aproximadamente que o proxy dinâmico do JDK acabará por gerar uma classe de proxy com a seguinte estrutura para nós:
classe pública proxy0 estende o proxy implementa o userdao {// etapa 1, gera proxy0 protegido do construtor (invocationHandler h) {super (h); } // Etapa 2, gerar domínio estático Método estático privado m1; // Método HashCode Método estático privado M2; // é o método estático privado m3 privado; // Método de ToString Método estático privado M4; // ... // Etapa 3, gerar método proxy @Override public int hashCode () {try {return (int) h.invoke (this, m1, null); } catch (throwable e) {lança uma nova unsCarwarThrowableException (e); }} @Override public boolean é igual (objeto obj) {try {object [] args = new Object [] {obj}; retornar (booleano) H.invoke (isto, m2, args); } catch (throwable e) {lança uma nova unsCarwarThrowableException (e); }} @Override public string tostring () {try {return (string) h.invoke (this, m3, null); } catch (throwable e) {lança uma nova unsCarwarThrowableException (e); }} @Override public void Salvar (usuário do usuário) {tente {// Construa a matriz de parâmetro, se houver vários parâmetros adicionados posteriormente, apenas objeto [] args = new Object [] {user}; h.invoke (isto, m4, args); } catch (throwable e) {lança uma nova unsCarwarThrowableException (e); }} // Etapa 4, gerar o método de inicialização estática estática {try {classe c1 = classe.ForName (object.class.getName ()); Classe C2 = classe.ForName (userdao.class.getName ()); m1 = c1.getMethod ("hashcode", nulo); m2 = c1.getMethod ("equals", nova classe [] {object.class}); m3 = c1.getMethod ("tostring", nulo); m4 = c2.getMethod ("salvar", nova classe [] {user.class}); // ...} catch (Exceção e) {e.printStackTrace (); }}}Nesse ponto, após a análise em camadas e a exploração aprofundada do código-fonte do JDK, restauramos a aparência original da classe proxy gerada dinamicamente, e algumas das perguntas anteriores também foram bem explicadas.
1. A classe proxy herda a classe Porxy por padrão. Como o Java suporta apenas a herança única, o proxy dinâmico do JDK pode implementar apenas interfaces.
2. Os métodos proxy chamarão o método Invoke () de InvocationHandler, por isso precisamos reescrever o método Invoke () de InvocationHandler.
3. Ao chamar o método Invoke (), a própria instância de proxy, o método de destino e os parâmetros do método de destino serão transmitidos. Explique como os parâmetros do método Invoke () vêm.
Use o proxy0 recém -construído como a classe proxy para testar novamente, e você pode ver que o resultado final é o mesmo que a classe proxy gerada dinamicamente usando o JDK. Mais uma vez, nossa análise é confiável e precisa. Neste ponto, os artigos da série Dynamic Proxy JDK foram anunciados para terminar. Através da análise desta série, o autor resolveu as dúvidas de longa data em seu coração, e acredito que a compreensão dos leitores sobre o proxy dinâmico do JDK se tornou um passo adiante. No entanto, o conhecimento no papel é sempre superficial. Se você deseja melhor dominar a tecnologia de proxy dinâmica do JDK, os leitores podem se referir a esta série de artigos para verificar o código -fonte do JDK por si mesmos, ou trocar experiência de aprendizado com o autor, apontar a análise inadequada do autor, aprender juntos e progredir juntos.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.