El modelo proxy es un modelo de diseño Java de uso común. Su característica es que la clase proxy y la clase delegada tienen la misma interfaz. La clase proxy es el principal responsable del preprocesamiento de mensajes, filtrando mensajes, enviando mensajes a la clase delegada y procesar mensajes después del evento. Por lo general, hay una asociación entre la clase proxy y la clase delegada. El objeto de una clase proxy está asociado con el objeto de una clase delegada. El objeto de la clase proxy en sí no implementa realmente el servicio, sino que proporciona servicios específicos llamando a los métodos relevantes del objeto de clase delegada.
Comparación de varias implementaciones proxy dinámicas de Java
interfaz
interfaz addinterface {int add (int a, int b);} subinterface de interfaz {int sub (int a, int b);} Clase de implementación
clase 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 integrado en JDK
1. Método de implementación
El mecanismo de proxy dinámico introducido por Java después de JDK1.3 nos permite crear dinámicamente clases proxy durante el tiempo de ejecución. La implementación de AOP utilizando proxy dinámico requiere cuatro roles: la clase proxy, la interfaz de clase proxy, el dispositivo de tejido y el invocadorhandler. El dispositivo de tejido utiliza el mecanismo de reflexión de la interfaz para generar una clase proxy y luego tejer código en esta clase de proxy. La clase proxy es el objetivo mencionado en AOP. InvocationHandler es una sección, que contiene consejos y puntos de punto.
2. Implementación de la interfaz de VinVocationHandler
clase JDKDPQueryHandler implementa InvocationHandler {Private Arithmetic real; public jdkdpQueryHandler (aritmetic real) {this.real = real; } @Override public Object Invoke (Object Proxy, Method Method, Object [] args) lanza lando {string metodName = método.getName (); System.out.println (método); System.out.println ("El método:" + MethodName + "Inicio, Parámetros:" + Arrays.aslist (args)); Resultado del objeto = método.invoke (real, args); System.out.println ("El método:"+MethodName+"finaliza, resultado:"+resultado); resultado de retorno; }}3. Cree una clase proxy y llame a la clase proxy
clase pública Main {private static int a = 4, b = 2; Public static object createJDKProxy (aritmetic real) {object proxyarithmetic = proxy.newproxyInstance (real.getclass (). getClassLoader (), real.getclass (). getInterfaces (), nuevo JDKDPQueryHandler (real)); return proxyarithmetic; } public static void main (string [] args) {arithmetic real = new arithmetic (); Objeto proxyarithmetic = createJDKProxy (real); ((Addinterface) proxyarithmetic) .add (a, b); ((Subinterfaz) proxyarithmetic) .sub (a, b); }}Método 2: Generación de Bytecode Dynamic (CGLIB)
1. Método de implementación
Potenciador y metoderceptor. El potenciador se puede usar para generar dinámicamente una clase, que puede heredar una clase especificada e implementar algunas interfaces especificadas. Al mismo tiempo, el potenciador necesita especificar una devolución de llamada antes de generar una clase. Cuando se llama al método de clase, la ejecución del método se asigna a esta devolución de llamada. MethodInterceptor es una interfaz más utilizada heredada de la devolución de llamada, y solo tiene una declaración de método.
2. Comparación de Interface InvocationHandler (en JDK) y Methodinterceptor de interfaz (en CGLIB)
Methodinterceptor de la interfaz pública extiende la devolución de llamada {Intercept de objeto público (object obj, java.lang.reflect.method método, object [] args, métodeProxy proxy) lanza lunes; } Public Interface InvocationHandler {Public Object Invoke (Object Proxy, Method Method, Object [] args) lanza Throwable; } En términos de composición de parámetros, los parámetros de entrada del Interceptor de Métodos son 1 más que InvocationHandler. De hecho, el significado de los primeros tres objetos de parámetros es el mismo que el de InvocationHandler.
El primer parámetro indica de qué objeto proviene el método de llamada;
El segundo parámetro representa el objeto del método que llama al método;
El tercer parámetro representa la lista de parámetros de entrada para esta llamada;
Los parámetros adicionales son de tipo MethodProxy, que debe ser un objeto generado por CGLIB para reemplazar el objeto del método. El uso de MethodProxy mejorará la eficiencia que llamar directamente al método propio del método del JDK.
3. Date cuenta 1
Implementación de la interfaz de MethodInterceptor
clase CGLIBDPQueryInterceptor implementa MethodInterceptor {Private Arithmetic real; public cglibdpQueryInterceptor (aritmética real) {this.real = real; } @Override Public Object Intercept (Object Target, Method Method, Object [] Args, MethodProxy Proxy) lanza Throwable {String MethodName = Method.getName (); System.out.println (método); System.out.println ("El método:" + MethodName + "Inicio, Parámetros:" + Arrays.aslist (args)); // resultado de objeto = método.invoke (real, args); // Ambos métodos pueden obtener el resultado del objeto = proxy.invoke (real, args); System.out.println ("El método:"+ MethodName+ "finaliza, resultado:"+ resultado); resultado de retorno; }}Crear una clase proxy y llamar a una clase proxy
clase pública Main {private static int a = 4, b = 2; objeto estático público createCglibproxy (aritmética real) {mejor mejor potencador = new mejor (); potencer.setCallback (nuevo cglibdpQueryInterceptor (real)); potencer.setInterfaces (real.getclass (). getInterfaces ()); return mejor.create (); } public static void main (string [] args) {arithmetic real = new arithmetic (); Objeto proxyarithmetic = createCglibProxy (real); ((Addinterface) proxyarithmetic) .add (a, b); ((Subinterfaz) proxyarithmetic) .sub (a, b); }}Tenga en cuenta que MethodProxy proporciona 2 métodos al ejecutar funciones.
Public Object Invoke (Obj Obj, Object [] args) lanza el invocador de objeto público lanzable (objeto obj, objeto [] args) lanza lanzamiento
Entre ellos, el Javadoc dice que este método Invoke () se puede utilizar para la ejecución de otros objetos en la misma clase, es decir, el OBJ en este método debe pasar a otro objeto de la misma clase (el método anterior es pasar a diferentes objetos de la clase aritmética), de lo contrario, ingresará a un bucle recursivo infinito (el flujo de cerdo realmente aparece después de la prueba). Si lo piensa cuidadosamente, encontrará que el objetivo en el objeto público Intercept (objetivo de objeto, método método, objeto [] args, métodos proxy) es un objeto proxy implementado. Cuando el método add () se llama a través del objetivo, el método intercept () se activará para llamarse. Si el método.invoke (Target, Args) se llama en el método intercept (), es equivalente a llamar al método add () nuevamente, lo que resulta en un bucle recursivo infinito. Pero si ejecuta Method.Invoke (Real, Args) no lo hará, porque Real y Target son objetos diferentes en la misma clase, Real es el tema lógico real, y el objetivo es el proxy para el tema real real.
Aquí hay un ejemplo para simular:
Interface SolveInterface {void solve ();} class Real implements SolveInterface {public void solve () {System.out.println ("Real Solve!"); }} El objetivo de clase se extiende {objero privado obj; public void setObject (objeto obj) {this.obj = obj; } private void invoke () {try {método método = solveinterface.class.getmethod ("solve", nueva clase [] {}); método.invoke (obj, nueva clase [] {}); } catch (Exception e) {E.PrintStackTrace (); }} public void solve () {System.out.println ("¡Solve de destino!"); invocar(); }} public class Main {public static void main (string [] args) arroja excepción {objetivo objetivo = nuevo objetivo (); Target.SetObject (new Real ()); // correcto // Target.SetObject (Target); // Cyclic Call Ocurre Target.solve (); }}De hecho, el método Invoke () del método llamará al método Solve () correspondiente de acuerdo con el tipo OBJ, es decir, el polimorfismo.
4. Date cuenta 2
Implementación de la interfaz de MethodInterceptor
clase CGLIBDPQueryInterceptor implementa MethodInterceptor {@Override Public Object Intercept (Object Target, Method Method, Object [] Args, MethodProxy Proxy) lanza lando {string methodName = método.getName (); System.out.println (método); System.out.println (método); System.out.println ("El método:" + MethodName + "Inicio, Parámetros:" + Arrays.aslist (args)); // Información de clase de impresión: Target.getClass (); omitir el resultado del objeto = proxy.Invokesuper (Target, Args); System.out.println ("El método:"+MethodName+"finaliza, resultado:"+resultado); resultado de retorno; }}Crear una clase proxy y llamar a una clase proxy
clase pública main {private static int a = 4, b = 2; objeto estático público createCglibProxy () {mejor mejor mejor = new mejor (); potencador.setCallback (nuevo cglibdpQueryInterceptor ()); potencador.setsuperClass (arithmetic.class); return mejor.create (); } public static void main (string [] args) {arithmetic real = new arithmetic (); Objeto proxyarithmetic = createCglibProxy (); ((Addinterface) proxyarithmetic) .add (a, b); ((Subinterfaz) proxyarithmetic) .sub (a, b); }}Tenga en cuenta que el potenciador no establece la interfaz en la implementación 2, porque la superclase está establecida (es decir, la clase principal de la clase proxy es aritmética), nuestra clase proxy la heredará y la aritmética ha implementado nuestra interfaz. Para probar esto, puede imprimir la información de clase de Target.getClass () en el método Intercept of MethodInterceptor. Encontrará que la clase principal de la clase de proxy CGLIB es diferente. como sigue:
Implementación 1:
clase pública com.test.arithmetic $$ mentorbycglib $$ 4fa786eb extiende java.lang.object
Implementación 2:
clase pública com.test.arithmetic $$ mejorbycglib $$ 4fa786eb extiende com.test.arithmetic
Método 3: Javassist genera proxy dinámico (creación de fábrica de agentes o creación de código dinámico)
Javassist es un marco para editar bytecode, que le permite operar bytecode de manera muy simple. Puede definir o modificar la clase durante el tiempo de ejecución. El principio de implementar AOP usando Javassist es modificar directamente el método que debe cortarse antes de cargar el código de bytEd. Esto es más eficiente que usar CGLIB para implementar AOP, y no hay muchas restricciones. El principio de implementación es el siguiente:
Implementación 1:
Implementación de interfaces
clase javassistdpQueryHandler implementa métodoshandler {@Override public Object Invoke (Object Target, Method Method, Method Proxy, Object [] args) lanza {string MethodName = Method.getName (); System.out.println (método); System.out.println (método); System.out.println ("El método:" + MethodName + "Inicio, Parámetros:" + Arrays.aslist (args)); Resultado del objeto = proxy.invoke (objetivo, args); System.out.println ("El método:"+MethodName+"finaliza, resultado:"+resultado); resultado de retorno; }}Crear una clase proxy y llamar a una clase proxy
clase pública Main {private static int a = 4, b = 2; objeto estático público createjavassistproxy () lanza la excepción {proxyFactory factory = new ProxyFactory (); fábrica.setsuperclass (arithmetic.class); Factory.Sethandler (New JavassistdpQueryHandler ()); return factory.createClass (). NewInstance (); } public static void main (string [] args) lanza la excepción {arithmetic real = new arithmetic (); Objeto proxyarithmetic = createJavassistProxy (); ((Addinterface) proxyarithmetic) .add (a, b); ((Subinterfaz) proxyarithmetic) .sub (a, b); }}Nota: La definición del método Invoke en la interfaz MethodHandler es la siguiente:
Public Object Invoke (objetivo de objeto, método, método, proxy, objeto [] args)
El método representa el objeto del método que llama al método, proxy es el objeto que genera y reemplaza el método por clase proxy. De lo contrario, el uso de Method.Invoke (Target, Args) generará una llamada de bucle infinito.
Implementación 2:
El proceso proxy común de usar Javassist dinámico es ligeramente diferente del método anterior. Dentro de Javassist, se puede generar código Java dinámico para generar bytecode. El proxy dinámico creado de esta manera puede ser muy flexible e incluso puede generar lógica comercial en tiempo de ejecución.
// Interceptor Interceptor InterceptorHandler { /*** Llamar al método de objeto proxy dinámico reflejará este método. Puede agregar pre y postoperaciones de AOP a la implementación de este método. Solo si se agrega el siguiente código a este cuerpo del método* El método proxy se ejecutará, y el valor de retorno se devolverá al proxy y finalmente se devuelve al programa* @param obj object el objeto proxy* @param método método el objeto proxy* @param objeto [] parámetros del objeto proxy* @return objeto de retorno después del valor de ejecución del método de la ejecución del método @ththeats de objeto publicado* obj, método método, objeto [] args) lanza lanzamiento; } // Implementación de la clase Interceptor InterceptorHandlerImpl implementa InterceptorHandler {@Override Public Object Invoke (Object obj, Method Method, Object [] args) lanza lando {string metodName = método.getName (); System.out.println (método); System.out.println (método); System.out.println ("El método:" + MethodName + "Inicio, Parámetros:" + Arrays.aslist (args)); Resultado del objeto = método.invoke (obj, args); System.out.println ("El método:"+MethodName+"finaliza, resultado:"+resultado); resultado de retorno; }} class myProxyImpl { / ** Nombre de clase sufijo de la clase dinámica de proxy* / private final static string proxy_class_name_suffix = "$ myproxy_"; / ** Interfaz Interceptor*/ String static final privada Interceptor_handler_interface = "com.test.interceptorHandler"; / ** El índice de nombre de clase de la clase proxy dinámica para evitar la duplicación de los nombres de clases*/ private static int proxyClassIndex = 1; /*** La interfaz de proxy dinámica expuesta al usuario, devuelve el objeto proxy dinámico de una determinada interfaz. Tenga en cuenta que la implementación de este proxy debe usarse con el Interceptor de com.cuishen.myaop.interceptorHandler*, es decir, si el usuario desea usar este proxy dinámico, primero debe implementar el nombre de la clase de interfaz de interface de interfaceclass. * @param ClasStoProxy String El nombre de clase de la clase de implementación de la interfaz para que se proxye dinámicamente, por ejemplo, test.studentInfoserviceImpl * @param interceptorHandlerImplClassName String el nombre de clase de la clase de implementación de la interfaz interceptor proporcionada por el usuario * @return devuelve el objeto de proxy dinámico de una cierta interfacio * @throws instituction * IlegalAccessException * @throws NotFoundException * @throws no se puede hacer CompileException * @throws classNotFoundException * @see com.cuishen.myaop.intercepteRhandler */ public static Object NewProxyInstance (String InterFacecLassNnEnsnEnsnEn. IlegalAccessException, NotFoundException, no se puede competir conexception, classnotFoundException {class interfaceClass = class.forname (interfaceClassName); Class InterceptorHandlerImplass = class.forname (InterceptorHandlerImplClassName); return DynamicImplementsInterface (ClasStoProxy, InterFacecLass, InterceptorHandlerImplass); } /** * Implementación dinámica de la interfaz para ser proxyed * @param ClasStoProxy String El nombre de clase de la clase de implementación de la interfaz de la interfaz para que sea dinámicamente proxyed, por ejemplo, test.studentInfosServiceImpl * @param interfaceclass interfac Clase de implementación proporcionada por el usuario de la interfaz del interceptor * @return Object Devuelve el objeto proxy dinámico de una cierta interfaz * @throws noFoundException * @throws no puedo compileException * @throws instantiationException * @throws ilegalaccesexception */ private static objectiMplementInterface (string classtoproxy, class interfaceclass, clase interceptora interceptora NotFoundException, no puedo competir, ENSTANTISTIAMEXCeption, ilegalAccessException {classpool cp = classpool.getDefault (); String interfaCename = interfaceClass.getName (); // Dynamicly especifique el nombre de clase de la cadena de clase Proxy ProxyClassName = interfacename + proxy_class_name_suffix + proxyClassIndex ++; // El nombre del paquete de la interfaz que se implementará + Nombre de la interfaz 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étodss.length; i ++) {método método = métodos [i]; DynamicImplementsMethodsFromInterface (ClasStoProxy, CC, Method, InterceptorHandlerImplss, I); } return (objeto) cc.toclass (). newInStance (); } /*** Método en la implementación dinámica del método* @param ClasStoProxy String El nombre de clase de la clase de implementación de la interfaz para que se proxye dinámicamente, por ejemplo, test.studentInFoServiceImpl* @param implementador ctClass el empaquetado de la clase dinámica de proxy* Clase de InterceptIclass La clase de implementación del interceptor proporcionada por el usuario* @param método INT IND INDEX del método que se implementará* @throws no puede compileException*/ private static void dynamicImplementsMethodsFromInterface (String ClasStOproxy, CTClass implementador, método MethodToImpl, classpLass InterceptCloass, int Methodex) showings showeCompileEte -striETCode {strietCode Methodcode, MethodCode = GenerateMethodcode (ClasStoProxy, MethodToImpl, InterceptorClass, MethodIdex); Ctmethod cm = ctnewmethod.make (métodocode, implementer); implementer.addmethod (cm); } /*** Ensamble dinámicamente el cuerpo del método. Por supuesto, la implementación del método en el proxy no es una copia del método simple, sino que refleja el método de invoke en el interceptor y pasa los parámetros recibidos* @param ClasStoProxy String El nombre de clase de la clase de implementación de la interfaz para que sea dinámicamente proxyed, eg test.studentServiceImpl* @Param Methodtoppl toImpl. @Param InterceptorClass Clase La clase de implementación del interceptor proporcionado por el usuario* @param MethodIndex int índice del método que se implementará* @return cadena la cadena del método de ensamblaje dinámico*/ private static string genereMethodCode (string clasStOproxy, método método, clase interceptorClass, int métodyx) {string methodname = métodos MethodPeM.getName () ();) String MethodReturnType = MethodToImpl.getReturnType (). GetName (); Clase [] parámetros = métodoToImpl.getParametertypes (); Clase [] excepcionTypes = métodoImpl.getExceptionTypes (); StringBuffer ExceptionBuffer = new StringBuffer (); // Declaración de excepción del método de ensamblaje if (ExceptionTypes.length> 0) ExceptionBuffer.append ("Throws"); for (int i = 0; i <excepcionTypes.length; i ++) {if (i! = ExceptionTypes.length - 1) ExceptionBuffer.Append (ExceptionTypes [i] .getName ()). Append (","); else ExceptionBuffer.append (ExceptionTypes [i] .getName ()); } StringBuffer ParameterBuffer = new StringBuffer (); // Lista de parámetros del método de ensamblaje para (int i = 0; i <parámetros.length; i ++) {class parameter = parámetros [i]; String Parametertype = parameter.getName (); // Dynamic especificando el nombre de la variable del parámetro de método String refname = "A" + i; if (I } StringBuffer MethodDeclare = new StringBuffer (); // Declaración del método, ya que es un método que implementa la interfaz, es público MethodDeclare.Append ("public") .Append (MethodreturnType) .Append ("") .Append (MethodName) .Append ("("). Append (ParameterBuffer) .Append (")"). APPEND (ExceptionBuffer) .Append ("{/n"); String interceptorImplname = interceptorclass.getName (); // Methodbody MethodDeclare.Append (Interceptor_handler_interface) .Append ("Interceptor = New") .Append (InterceptorImplname) .Append ("();/n"); // Reflexión de la interfaz de interceptor del usuario del usuario MethodDeClare.append ("Object ReturnObj = interceptor.invoke (class.forname (/" " + ClasStoProxy +"/"). NewInstance (), class.forname (/" " + ClasStoProxy +"/"). GetMethods () [" + "Methodex +"], "); if (parámetros.length> 0) métodos declare.append ("nuevo objeto [] {"); MethodDeclare.append ("($ w) a" + i + ","); else MethodDeclare.append ("nulo);/n"); // Envuelve el valor de retorno del interceptor de llamadas if (métodoToImpl.getReturnType (). IsPrimitive ()) {if (métodoToImpl.getReturnType (). Equals (boolean.type)) MethodDeClare.Append ("return ((boolean) returnobj) .booleNValue ();/n");; else if (métodoToImpl.getReturnType (). Equals (Integer.Type)) MethodDeClare.Append ("return ((Integer) returnObj) .intvalue ();/n"); else if (métodoToImpl.getReturnType (). Equals (long.type)) MethodDeclare.append ("return ((long) returnObj) .longValue ();/n"); else if (métodoToImpl.getReturnType (). Equals (float.type)) MethodDeclare.append ("return ((float) returnObj) .floatValue ();/n"); más if (métodoToImpl.getReturnType (). Equals (Double.Type)) MethodDeClare.Append ("return (" return ("return (" return ("return ();/n"); else if (métodoToImpl.getTreturnType (). Equals (Double.Type))) MethodDeClare.Append ("return (" return ("return (" return ("return (" return ();/n "); else if (métodoImpl.getReturnType (). Equals (double.type)) MethodDeClare.Append (" return ("return (" return ("return (" return ("return (" return ("MethodToImpL.getReturnType (). Equals (). MethodDeClare.append ("return (" return ("return (" return ("return (" métodoToImpl.getReturnType (). Equals (double.type)) MethodDeClare.append ("return (" return ("return (" return ("return (" metodToiMpl.getReturnType (). Equals (double.type) MethodDeClare.Append ("return (" return ("return (" return ("métodoImpl.getReturnType (). igual (((doble) returnObj) .DoubleValue ();/n"); else if (métodoToIm.getTretReturnType (). igual (caracteres.type)) método declare.append ("return ((caracteres) returnObl); else if (métodeToImpl.getReturnType (). Equals (byte.type)) MethodDeClare.append ("return ((byte) returnObj) .byTeValue ();/n"); ((Byte) returnObj) .ByTeValue ();/n "); else if (métodoToImpl.getTreturnType (). Equals (short.type)) MethodDeClare.append (" return ((short) returnObj) .ShortValue ();/n ");} else {MethodDeclare.Append (" return (" + MethodeTrurnTurn ") returnObj;/n"); Se requiere la clase proxy, el nombre de clase de implementación del interceptor definido por el usuario objeto Proxyarithmetic = myProxyImpl.NewProxyInstance ("com.test.arithmeticinterface", "com.test.arithmetic", "com.test.InterceptorHandlerImpl"); ((Aritmética Interfaz) proxyarithmetic) .sub (a, b);Imprima el código para implementar dinámicamente la interfaz de la siguiente manera:
public int add (int a0, int a1) {com.test.interceptorhandler interceptor = new com.test.interceptorHandlerImpl (); object returnObj = interceptor.invoke (class.forname ("com.test.arithmetic"). NewInStance (), class.forname ("com.test.arithmetic"). Objeto [] {($ w) a0, ($ w) a1}); return ((integer) returnObj) .intvalue ();} public int sub (int a0, int a1) {com.test.interceptorhandler interceptor = new com.test.interceptorHandlerImpl (); ObjetObj = interceptor.invoke (class.forname ("com.test.arithmetic"). NewInstance (), class.forname ("com.test.arithmetic"). getMethods () [1], nuevo objeto [] {($ w) a0, ($ w) a1}); regreso ((interger) returnobj) .intvalue ();};} Lo anterior es una introducción detallada al mecanismo de implementación de proxy dinámico en Java, y espero que sea útil para el aprendizaje de todos.