O modelo proxy é um modelo de design Java comumente usado. Sua característica é que a classe de proxy e a classe Delegate tenham a mesma interface. A classe de proxy é principalmente responsável por pré -processamento de mensagens, filtrar mensagens, encaminhar mensagens para a classe Delegate e processando mensagens após o evento. Geralmente, existe uma associação entre a classe proxy e a classe Delegate. O objeto de uma classe de proxy está associado ao objeto de uma classe delegada. O objeto da classe de proxy em si não implementa realmente o serviço, mas fornece serviços específicos chamando os métodos relevantes do objeto de classe Delegate.
Comparação de várias implementações dinâmicas de proxy de Java
interface
interface addInterface {int add (int a, int b);} interface subinterface {int sub (int a, int b);} Classe de implementação
classe Aritmética implementa AddInterface, subinterface {@Override public int sub (int a, int b) {return ab; } @Override public int add (int a, int b) {return a+b; }}Método 1: Proxy dinâmico incorporado ao JDK
1. Método de implementação
O mecanismo dinâmico de proxy introduzido pelo Java após o JDK1.3 nos permite criar classes de proxy dinamicamente durante o tempo de execução. A implementação da AOP usando proxy dinâmico requer quatro funções: a classe proxy, a interface da classe proxy, o dispositivo de tecelagem e o InvocationHandler. O dispositivo de tecelagem usa o mecanismo de reflexão da interface para gerar uma classe de proxy e, em seguida, tece o código nessa classe proxy. A classe de proxy é o alvo mencionado na AOP. InvocationHandler é uma seção, que contém conselhos e pontos de ponto.
2. Implementação da interface VinvocationHandler
classe jdkdpqueryhandler implementa InvocationHandler {private aritmética real; public jdkdpqueryHandler (aritmético real) {this.real = real; } @Override Public Object Invoke (proxy do objeto, método do método, objeto [] args) lança arremesso {string methodName = métod.getName (); System.out.println (método); System.out.println ("O método:" + MethodName + "Iniciar, parâmetros:" + Arrays.asList (args)); Resultado de objeto = method.invoke (real, args); System.out.println ("O método:"+MethodName+"termina, resultado:"+resultado); resultado de retorno; }}3. Crie uma aula de proxy e ligue para a classe de proxy
classe pública principal {private static int a = 4, b = 2; objeto estático público createjdkProxy (aritmético real) {objeto proxyarithmetic = proxy.newproxyInstance (real.getclass (). getClassloader (), real.getclass (). retornar proxiaritmético; } public static void main (string [] args) {aritmética real = new aritment (); Objeto proxyaritmetic = createjdkProxy (real); ((AddInterface) proxyaritmetic) .add (a, b); ((Subinterface) proxiaritmético) .sub (a, b); }}Método 2: Geração dinâmica de bytecode (CGLIB)
1. Método de implementação
Intensificador e método -interceptador. O intensificador pode ser usado para gerar dinamicamente uma classe, que pode herdar uma classe especificada e implementar algumas interfaces especificadas. Ao mesmo tempo, o intensificador precisa especificar um retorno de chamada antes de gerar uma classe. Quando o método da classe é chamado, a execução do método é atribuída a esse retorno de chamada. O MethodInterceptor é uma interface mais usada herdada do retorno de chamada e possui apenas uma declaração de método.
2. Comparação de InvocationHandler de interface (em JDK) e MethodInterceptor da interface (no CGLIB)
Public Interface MethodInterceptor estende o retorno de chamada {Public Object Intercept (Object Obj, java.lang.reflect.method Método, objeto [] args, Methodproxy proxy) joga arremessado; } Public Interface InvocationHandler {public Object Invoke (proxy do objeto, método do método, objeto [] args) lança Throwable; } Em termos de composição dos parâmetros, os parâmetros de entrada do MethodIntercept são 1 a mais que o InvocationHandler. De fato, o significado dos três primeiros objetos de parâmetro é o mesmo que o de InvocationHandler.
O primeiro parâmetro indica de qual objeto o método de chamada vem;
O segundo parâmetro representa o objeto de método que chama o método;
O terceiro parâmetro representa a lista de parâmetros de entrada para esta chamada;
Os parâmetros extras são do tipo MethodProxy, que devem ser um objeto gerado pelo CGLIB para substituir o objeto do método. O uso do MethodProxy melhorará a eficiência do que chamar diretamente o próprio método do próprio método do JDK.
3. Realize 1
Implementação da interface do MethodInterceptor
classe CGLIBDPQUERYInterceptor implementa MethodInterceptor {Private Aritmetic Real; public CGLIBDPQUERYIntercept (aritmético real) {this.real = real; } @Override Public Object Intercept (destino do objeto, método do método, objeto [] args, MethodProxy Proxy) lança arremesso {string methodName = method.getName (); System.out.println (método); System.out.println ("O método:" + MethodName + "Iniciar, parâmetros:" + Arrays.asList (args)); // objeto resultado = method.invoke (real, args); // Ambos os métodos podem obter resultado do objeto = proxy.invoke (real, args); System.out.println ("O método:"+ MethodName+ "termina, resultado:"+ resultado); resultado de retorno; }}Crie uma aula de proxy e ligue para uma aula de proxy
classe pública principal {private static int a = 4, b = 2; Public Static Object CreateCglibProxy (aritmético real) {intensificador intensificador = new ENFERCHER (); intensancer.setCallback (novo CGLIBDPQUERYINTERCECTOR (REAL)); intensancer.setInterfaces (real.getClass (). getInterfaces ()); return aprimor.create (); } public static void main (string [] args) {aritmética real = new aritment (); Objeto proxyaritmetic = createCglibProxy (real); ((AddInterface) proxyaritmetic) .add (a, b); ((Subinterface) proxiaritmético) .sub (a, b); }}Observe que o MethodProxy fornece 2 métodos ao executar funções.
Public Object Invoke (objeto obj, objeto [] args) lança o objeto público arremessado Invokesuper (objeto obj, objeto [] args) lança arremesso
Entre eles, o Javadoc diz que esse método Invoke () pode ser usado para a execução de outros objetos na mesma classe, ou seja, o OBJ nesse método precisa passar para outro objeto da mesma classe (o método acima é passar para diferentes objetos da classe aritmética), caso contrário, ele entrará em um loop recursivo infinito (StackOverflower realmente parecerá. Se você pensar com cuidado, descobrirá que o alvo na interceptação de objeto público (destino do objeto, método do método, objeto [] args, MethodProxy Proxy) é um objeto de proxy implementado. Quando o método add () é chamado através do destino, o método intercept () será acionado para ser chamado. Se o método.invoke (alvo, args) for chamado no método intercept (), é equivalente a chamar o método add () novamente, resultando em um loop recursivo infinito. Mas se você executar o método.invoke (real, args) não, porque real e alvo são objetos diferentes na mesma classe, o Real é o tópico lógico real, e o Target é o proxy para o tópico real real.
Aqui está um exemplo para simular:
interface solveInterface {void solve ();} classe de implementos reais solveInterface {public void solve () {System.out.println ("Real Solve!"); }} classe Target estende real {objeto privado obj; public void setObject (object obj) {this.obj = obj; } private void Invoke () {try {Method Method = resolveInterface.class.getMethod ("Solve", nova classe [] {}); métod.invoke (obj, nova classe [] {}); } catch (Exceção e) {e.printStackTrace (); }} public void solve () {System.out.println ("Target Solve!"); invocar(); }} classe pública Main {public static void main (string [] args) lança exceção {Target Target = new Target (); Target.setObject (new Real ()); // correto // Target.setObject (Target); // chamado cíclico ocorre Target.solve (); }}De fato, o método Invoke () do método chamará o método Solve () correspondente de acordo com o tipo OBJ, ou seja, polimorfismo.
4. Realize 2
Implementação da interface do MethodInterceptor
classe CGLIBDPQUERYInterceptor implementa MethodInterceptor {@Override Public Object Intercept (destino do objeto, método do método, objeto [] args, MethodProxy Proxy) lança Throwable {String MethodName = MethodName = MethodName (); System.out.println (método); System.out.println (método); System.out.println ("O método:" + MethodName + "Iniciar, parâmetros:" + Arrays.asList (args)); // Informações da classe de impressão: Target.getClass (); omita resultado do objeto = proxy.invokesuper (destino, args); System.out.println ("O método:"+MethodName+"termina, resultado:"+resultado); resultado de retorno; }}Crie uma aula de proxy e ligue para uma aula de proxy
classe pública main {private static int a = 4, b = 2; public static objeto createCglibProxy () {intensificador intensificador = new intensancer (); intensancer.setCallback (novo CGLIBDPQUERYINTERCECTOR ()); intensificador.SetSuperclass (aritmetic.class); return aprimor.create (); } public static void main (string [] args) {aritmética real = new aritment (); Objeto proxyaritmetic = createCglibProxy (); ((AddInterface) proxyaritmetic) .add (a, b); ((Subinterface) proxiaritmético) .sub (a, b); }}Observe que o intensificador não define a interface na implementação 2, porque a Superclass está definida (ou seja, a classe pai da classe proxy é aritmética), nossa classe de proxy o herdará e a aritmética implementou nossa interface. Para provar isso, você pode imprimir as informações da classe do Target.getClass () no método de interceptação do MethodInterceptor. Você descobrirá que a classe dos pais da classe CGLIB é diferente. do seguinte modo:
Implementação 1:
classe pública com.test.arithmetic $$ aprimorycglib $$ 4fa786eb estende java.lang.object
Implementação 2:
Public Class com.test.arithmetic $$ ENFERCHERBYCGLIB $$ 4FA786EB Estende com.test.aritmetic
Método 3: Javassist gera proxy dinâmico (criação de fábrica de agentes ou criação de código dinâmico)
O Javassist é uma estrutura para editar o ByteCode, que permite operar o ByteCode de maneira muito simples. Ele pode definir ou modificar a classe durante o tempo de execução. O princípio da implementação da AOP usando o Javassist é modificar diretamente o método que precisa ser cortado antes que o bytecode seja carregado. Isso é mais eficiente do que usar o CGLIB para implementar a AOP e não há muitas restrições. O princípio da implementação é o seguinte:
Implementação 1:
Implementação de interfaces
classe javassistdpQueryHandler implementa o MethodHandler {@Override public Object Invoke (destino do objeto, método do método, proxy do método, objeto [] args) lança jogável {string methodName = method.getName (); System.out.println (método); System.out.println (método); System.out.println ("O método:" + MethodName + "Iniciar, parâmetros:" + Arrays.asList (args)); Resultado do objeto = proxy.invoke (alvo, args); System.out.println ("O método:"+MethodName+"termina, resultado:"+resultado); resultado de retorno; }}Crie uma aula de proxy e ligue para uma aula de proxy
classe pública principal {private static int a = 4, b = 2; public static objeto createjavassistproxy () lança exceção {proxyfactory factory = new proxyfactory (); Factory.SetSuperclass (aritmetic.class); Factory.Sethandler (New JavassistdpQueryHandler ()); retornar fábrica.createclass (). NewInstance (); } public static void main (string [] args) lança exceção {aritmética real = new aritmense (); Objeto proxyaritmetic = createJavassistProxy (); ((AddInterface) proxyaritmetic) .add (a, b); ((Subinterface) proxiaritmético) .sub (a, b); }}NOTA: A definição do método Invoke na interface MethodHandler é a seguinte:
Public Object Invoke (destino do objeto, método do método, proxy do método, objeto [] args)
O método representa o objeto de método que chama o método, o proxy é o objeto que gera e substitui o método por classe proxy. Caso contrário, o uso do Method.inVoke (Target, args) gerará uma chamada de loop infinita.
Implementação 2:
O processo de proxy comum de usar o javassista dinâmico é ligeiramente diferente do método anterior. Dentro do Javassist, o código Java dinâmico pode ser gerado para gerar bytecode. O proxy dinâmico criado dessa maneira pode ser muito flexível e pode até gerar lógica de negócios em tempo de execução.
// Interface interceptor personalizada InterceptorHandler { /*** Chamando o método do objeto dinâmico de proxy refletirá esse método. Você pode adicionar pré e pós-operações semelhantes à AOP à implementação deste método. Somente se o código a seguir for adicionado a esse corpo do método* O método proxy será executado, e o valor de retorno será retornado ao proxy e finalmente retornado ao programa* @param obj objeto O objeto proxy* @param método do objeto* @@param o objeto de objeto de proxy* @ @@param o objeto de proxy* OBJ, método do método, objeto [] args) lança arremesso; } // Implementação da classe interceptores interceptHandLeRImpl implementa interceptorHandler {@Override Public Object Invoke (objeto obj, método do método, objeto [] args) lança arremesso {string methodName = métod.getName (); System.out.println (método); System.out.println (método); System.out.println ("O método:" + MethodName + "Iniciar, parâmetros:" + Arrays.asList (args)); Resultado de objeto = method.invoke (obj, args); System.out.println ("O método:"+MethodName+"termina, resultado:"+resultado); resultado de retorno; }} classe myProxyImpl { / ** classe Nome sufixo da classe dinâmica de proxy* / string estática final privada proxy_class_name_suffix = "$ myProxy_"; / ** Interface interceptora*/ String estática final privada interceptores interceptor_handler_Interface = "com.test.interceptHandler"; / ** O índice de nome da classe da classe dinâmica de proxy para evitar a duplicação de nomes de classe*/ private static int proxyclassindex = 1; /*** A interface dinâmica de proxy exposta ao usuário, retorna o objeto dinâmico de proxy de uma determinada interface. Observe que a implementação desse proxy deve ser usada com o interceptor com.cuishen.myaop.InterceptHandler*, ou seja, se o usuário deseja usar esse proxy dinâmico, ele deve primeiro implementar o com.cuishen.myaop.intercepthandler interceptor interface* @paramsiCEnSname string * @param classtoproxy string O nome da classe da classe de implementação da interface é dinamicamente proxyed, por exemplo, test.studentInfoserviceImpl * @param intercetorHandleRimplClassName Strin o nome da classe da classe de implementação da interface interceptor fornecida pela interface * * @Exturn IllegalAccessException * @throws NotFoundException * @throws CannotCompileException * @throws ClassNotFoundException * @see com.cuishen.myAop.InterceptorHandler */ public static Object newProxyInstance(String interfaceClassName, String classToProxy, String interceptorHandlerImplClassName) throws InstantiationException, IllegalAccessException, NotfoundException, não é possível comcompileException, classNotFoundException {classe interfaceclass = class.ForName (interfaceclassName); Classe interceptorHandLeRImplClass = Class.ForName (interceptorHandlerImplClassName); retornar dynamicimplementsInterface (classtoproxy, interfaceclass, interceptHandlerImplClass); } /** * Implementação dinâmica da interface a ser proxyed * @param classtoproxy string O nome da classe da classe de implementação da interface é dinamicamente proxyed, eg teste. Classe de implementação fornecida pelo usuário da interface interceptora * @return Objeto retorna o objeto dinâmico de proxy de uma determinada interface * @Throws NotfoundException * @Throws Can'tComComPileException * @Throws InstantiationException * @THROWSPROLACCESSEXCECTION */ STATION DynamicEmPlemormentsFace (StractStoproxy NotfoundException, não pode comcompileException, InstantionException, ilegalAccescessception {ClassPool cp = ClassPool.getDefault (); String interfacename = interfaceclass.getName (); // Especifique dinâmico o nome da classe da classe proxy string proxyclassName = interfacename + proxy_class_name_suffix + proxyclassindex ++; // o nome do pacote da interface a ser implementado + Nome da interface string interfacenamepath = interfacename; Ctclass ctInterface = cp.getctclass (interfacenamepath); Ctclass cc = cp.makeclass (proxyclassName); cc.addinterface (CTInterface); Método [] métodos = interfaceClass.getMethods (); for (int i = 0; i <métodos. DynamicImplementsMethodsFromInterface (CLASSTOPROXI, CC, Método, InterceptHandleRImplClass, I); } retornar (objeto) cc.toclass (). NewInstance (); } /*** Método na implementação dinâmica do método* @param classtoproxy string O nome da classe da classe de implementação da interface é dinamicamente proxyed, por exemplo, teste. Classe InterceptClass A classe de implementação do interceptador fornecida pelo usuário* @param MethodIndex int Índice do método a ser implementado* @THOWS não pode ser compilado*/ private estático void dynamicimplementsMethodsFromInterface (string clasStoproxy, ctclass implementador, MethodToImpl, classe intercetorclASTS, Int MethodSTOXY, implementador de métodos). generatemethodCode (classtoproxy, métodosToimpl, interceptClass, MethodIndex); Ctmethod cm = ctnewMethod.make (MethodCode, implementador); implement.addmethod (cm); } /*** monte dinamicamente o corpo do método. Of course, the method implementation in the proxy is not a simple method copy, but reflects the invoke method in the interceptor and passes the received parameters* @param classToProxy String The class name of the implementation class of the interface to be dynamically proxyed, eg test.StudentInfoServiceImpl * @param methodToImpl Method The wrapper of the interface method to be implemented in the dynamic proxy class* @Param InterceptClass Classe A classe de implementação do interceptador fornecida pelo usuário* @Param MethodIndex int Índice do método a ser implementado* @return string A sequência do método de montagem dinâmica*/ private static string generatemethodcode (string string (string cLastoxy, método MethodToImPl, classe interceptElPlASS, Int MetodIndEx) {String MethodEnMeTe; String MethodReturnType = MethodToimpl.getReturntype (). GetName (); Classe [] parâmetros = MethodToimpl.getParameterTypes (); Classe [] excepcionTypes = MethodToimpl.GetexceptionTypes (); StringBuffer ExceptionBuffer = new StringBuffer (); // Declaração de exceção do método de montagem if (excepctionTypes.Length> 0) ExceptionBuffer.Append ("Lança"); for (int i = 0; i <excepcionTypes.Length; i ++) {if (i! else ExceptionBuffer.Append (ExceptionTypes [i] .getName ()); } StringBuffer parameterBuffer = new StringBuffer (); // Lista de parâmetros do método de montagem para (int i = 0; i <parâmetros.length; i ++) {classe parâmetro = parâmetros [i]; String parameterType = parameter.getName (); // dinâmico especificando o nome da variável do parâmetro do método string refName = "a" + i; if (i! = parameters.length - 1) parameterbuffer.append (parameterType) .append (" + refName) .append (", "); else parameterbuffer.append (parameterType) .append (" + refname); } StringBuffer MethodDeclare = new StringBuffer (); Declaração do método, uma vez que é um método que implementa a interface, é public MethodDeclare.Append ("Public") .Append (MethodReturntype) .Append ("") .Append (MethodName) .Append ("("). Apênd (parameterBuffer) .Append ("). String interceptorImplName = interceptClass.getName (); // MethodBody MethodDeclare.Append (Interceptor_Handler_Interface) .Append ("Interceptor = new") .Append (interceptorImplName) .Append ("();/n"); // Reflexão Chamada de interface do usuário do usuário MethodDeclare.append ("Objeto returnObj = interceptor.invoke (classe.ForName (/" " + CLASSTOPROXY +"/"). NewInstance (), Class.Forname (/" " + CLASSTOXY +"/"). if (parâmetros.length> 0) MethodDeclare.append ("novo objeto [] {"); + i + ","); else métodDeclare.append ("null);/n"); // Enrole o valor de retorno do interceptador de chamada if (methodToimpl.getReturntype (). IsPrimitive ()) {if (methodToimpl.getrengtype (). Igual a (boolean.type)) métodDeclare.append ("return (boolean) returnObj). caso contrário, if (methodtoimpl.getRettype (). igual a (inteiro.type)) methodDeclare.append ("return ((integer) returnObj) .intValue ();/n"); caso contrário, if (methodtoimpl.getRettype (). igual a (long.type)) methodDeclare.append ("return ((long) returnObj) .longValue ();/n"); caso contrário, if (methodtoimpl.getRettype (). igual a (float.type)) methodDeclare.append ("return (((float) returnObj) .floatValue ();/n"); caso contrário, if (methodToimpl.getRenterType (). igual a (duplo.type)) methodDeclare.append ("return (" return ("return (" return ("return ();/n"); else if (methodToimpl.getRettype (). igual.type) MethodDeclare.Append ("return (" return ("return (" return ("return (" return ();/n "); else if (methodtoimpl.getRenterntype (). equals (Double.Type)) MethodDecLare.Append (" Return ("Return (" Return ("Return (" Return ("Return (" " MethodDeclare.Append ("return (" return ("return (" return ("return (" methodToimpl.getReturntype (). Equals (Double.Type)) MethodDeclare.append ("Return (" Return ("Return (" Return ("Return (" MethodTOImpl.GetRetType (). MethodDeclare.Append ("Return (" return ("return (" return ("MethodToImpl.getReturntype (). Equal ((Double) returnObj) .DoubleValue ();/n"); else (MethodToImpl. if (MethodToimpl.getRettype (). Equals (Byte.Type)) MethodDeclare.append ("Return ((byte) returnObj) .bytevalue ();/n"); ((Byte) returnObj) .byteValue ();/n "); else if (methodToImpl.getReturntype (). Equals (short.type)) methodDeclare.append (" return ((short) returnObj) .shortValue ();/n ");} else {MetodDecLare.ApnEpn). } MethodDeclare.append ("}"); Classe de implementação do interceptador Nome do objeto Proxyaritmetic = myProxyimpl.NewProxyInstance ("com.test.arithmeticinterface", "com.test.arithmetic", "com.test.intercepthandlerimpl"); ((AritmeticInterface) Proxyaritmetic) .Sub (a, b);Imprima o código para implementar dinamicamente a interface da seguinte forma:
public int add (int a0, int a1) {com.test.interceptHandler interceptor = new com.test.intercetorHandleRImpl (); objeto returnObj = interceptor.inVoke (classe.ForName ("com.test.arithmetic"). NewInstance (),. Objeto [] {($ w) a0, ($ w) a1}); return ((integer) returnObj) .IntValue ();} public int sub (int a0, int a1) {com.test.interceptHandler interceptor = new com.test.interceptHerHandlerImpl (); objeto returnObj = interceptor.inVoke (class.formaName ("com.test.arithmetic"). NewInstance (), class.ForName ("com.test.arithmetic"). getMethods () [1], novo objeto [] {($ w) a0, ($ w) a1}; O exposto acima é uma introdução detalhada ao mecanismo dinâmico de implementação de proxy em Java, e espero que seja útil para o aprendizado de todos.