Compartir tecnología

Explicación detallada del proxy: proxy estático, proxy dinámico, implementación de Spring AOP

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Introducción del agente

Proxy se refiere a un modo en el que un objeto A puede tener el mismo comportamiento que B al sostener otro objeto B. Para abrir el protocolo al mundo exterior, B a menudo implementa una interfaz y A también implementará la interfaz. Pero B es la clase de implementación "real", mientras que A es más "virtual". Toma prestados los métodos de B para implementar los métodos de la interfaz. Aunque A es un "pseudo ejército", puede mejorar B y hacer otras cosas antes y después de llamar al método de B. Spring AOP utiliza proxies dinámicos para completar el "tejido" dinámico del código.

Los beneficios de usar un proxy van más allá de esto. Si un proyecto depende de la interfaz proporcionada por otro proyecto, pero la interfaz del otro proyecto es inestable y el protocolo cambia con frecuencia, puede usar un proxy cuando la interfaz cambia. Solo es necesario modificar el proxy, no uno por uno. Modificar el código comercial. En este sentido, podemos hacer esto para todas las interfaces que se ajustan al mundo exterior para evitar que código externo invada nuestro código. Esto se llama programación defensiva. Puede haber muchas otras aplicaciones para el proxy.

En el ejemplo anterior, la clase A está codificada para contener B, que es el proxy estático de B. Si el objeto de un proxy es incierto, es un proxy dinámico. Actualmente existen dos implementaciones comunes de proxy dinámico, proxy dinámico jdk y proxy dinámico cglib.

2. Proxy estático

El proxy estático es un patrón de diseño y un tipo de patrón de proxy. En un proxy estático, la clase de proxy se define antes de ejecutar el programa y la relación entre la clase de proxy y la clase de proxy se determina en el momento de la compilación. Esto significa que tanto la clase proxy como la clase proxy implementan la misma interfaz o heredan la misma clase principal. La clase proxy contiene internamente una instancia de la clase proxy y llama a los métodos de la clase proxy en su propio método. , Puede agregar algunas de sus propias operaciones antes y después de la llamada, como registro, verificación de permisos, procesamiento de transacciones, etc.

El proxy estático tiene tres componentes: interfaz abstracta, clase de proxy y clase de proxy. Sus ejemplos de implementación son los siguientes:

1) Definir interfaz abstracta

  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }

2) Definir clase de proxy

  1. public class TargetProxy implements TargetInteface {
  2. private Target target =new Target();
  3. @Override
  4. public void method1() {
  5. System.out.println("执行方法前...");
  6. target.method1();
  7. System.out.println("执行方法后...");
  8. }
  9. @Override
  10. public void method2() {
  11. System.out.println("执行方法前...");
  12. target.method2();
  13. System.out.println("执行方法后...");
  14. }
  15. @Override
  16. public int method3(Integer i) {
  17. System.out.println("执行方法前...");
  18. int method3 = target.method3(i);
  19. System.out.println("执行方法后...");
  20. return method3;
  21. }
  22. }

3) Definir la clase de proxy

  1. public class Target implements TargetInteface {
  2. @Override
  3. public void method1() {
  4. System.out.println(" Target method1 running ...");
  5. }
  6. @Override
  7. public void method2() {
  8. System.out.println("Target method2 running ...");
  9. }
  10. @Override
  11. public int method3(Integer i) {
  12. System.out.println("Target method3 running ...");
  13. return i;
  14. }
  15. }

4) Definir el cliente y ver los resultados de la ejecución.

  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. TargetInteface target = new TargetProxy();
  4. target.method1();
  5. System.out.println("-----------------------------");
  6. target.method2();
  7. System.out.println("-----------------------------");
  8. System.out.println(target.method3(3));
  9. }
  10. }

Salida de resultados:

Antes de ejecutar el método...
Método de destino 1 en ejecución...
Después de ejecutar el método...
-----------------------------
Antes de ejecutar el método...
Método de destino2 ejecutándose...
Después de ejecutar el método...
-----------------------------
Antes de ejecutar el método...
Método de destino3 ejecutándose...
Después de ejecutar el método...
3

No es difícil ver en la implementación de agentes estáticos que las ventajas de los agentes estáticos son una implementación simple y fácil de entender. Pero sus deficiencias también son obvias, es decir, cada vez que necesita agregar funcionalidad de proxy a una nueva clase, debe crear manualmente una nueva clase de proxy, lo que provocará un fuerte aumento en el número de clases y un aumento en los costos de mantenimiento. . Al mismo tiempo, el grado de acoplamiento entre la clase de proxy y la clase de proxy es demasiado alto. Cuando se agregan, eliminan o modifican métodos en la clase de proxy, también se deben agregar, eliminar o modificar los métodos correspondientes en la clase de proxy. , lo que mejora el coste de mantenimiento del código. Otro problema es que cuando el objeto proxy representa las clases de implementación de múltiples interfaces de destino, debe haber diferentes métodos en las múltiples clases de implementación. Dado que el objeto proxy debe implementar la misma interfaz que el objeto de destino (en realidad, una relación de inclusión), debe implementar. Ser escrito Numerosos métodos pueden conducir fácilmente a un código inflado y difícil de mantener.

3. Proxy dinámico

La idea central del proxy dinámico es acceder indirectamente al objeto original a través del objeto proxy sin modificar el código del objeto original y realizar operaciones adicionales antes y después del acceso.

El principio de implementación del proxy dinámico se basa principalmente en el mecanismo de reflexión de Java. Cuando se utilizan servidores proxy dinámicos, es necesario definir una interfaz o un conjunto de interfaces que definan el comportamiento de la clase proxy (objeto proxy).Entonces, necesitas escribir una implementación.InvocationHandler Clase de interfaz, esta clase contiene lógica que se ejecuta antes y después de las llamadas al método en el objeto proxy.al llamarProxy.newProxyInstance()método, pase el cargador de clases de la interfaz, la matriz de la interfaz yInvocationHandlerObjeto, Java generará dinámicamente una clase proxy que implementa la interfaz especificada en tiempo de ejecución y delegará llamadas a métodos aInvocationHandler objetos a procesar.Cuando se llama a un método de un objeto proxy, en realidad llamaInvocationHandlerInterfazinvoke()Método, en el que puede realizar cierta lógica de preprocesamiento basada en el nombre del método, los parámetros y otra información, y luego llamar al método correspondiente del objeto proxy mediante la reflexión.

A continuación, presentamos dos proxies dinámicos: JDK Proxy y CGLib.

1) Proxy JDK

① Mecanismo interno de JDK Proxy

JDK Proxy utiliza el mecanismo de reflexión de Java para generar dinámicamente clases de proxy. Específicamente,Proxyla clase usaráProxyGenerator class (aunque esta clase no es una API pública, es la clave para implementar proxy dinámico dentro del JDK) para generar el código de bytes de la clase de proxy y cargarlo en la JVM.La clase de proxy generada heredará dejava.lang.reflect.Proxy clase e implementar la interfaz especificada.En el método de la clase proxy, se llamaráInvocationHandlerdeinvokeMétodo, reenvía la llamada al método al procesador para su procesamiento.

Además, para mejorar el rendimiento, JDK Proxy también proporciona un mecanismo de almacenamiento en caché para almacenar en caché el objeto Clase de la clase de proxy generada. De esta manera, cuando necesite crear un objeto proxy del mismo tipo, puede obtener directamente el objeto Clase de la clase proxy del caché sin regenerarlo.El almacenamiento en caché se realiza medianteWeakCacheImplementado por la clase, utiliza referencias débiles a objetos de caché para que los elementos de caché que ya no se utilizan se puedan limpiar automáticamente cuando la JVM realiza la recolección de basura.

② Pasos de implementación del proxy JDK

  • Definir interfaces y clases proxy.: Primero defina una o más interfaces, que serán implementadas por la clase proxy.
  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }
  1. public class Target implements TargetInteface {
  2. @Override
  3. public void method1() {
  4. System.out.println("method1 running ...");
  5. }
  6. @Override
  7. public void method2() {
  8. System.out.println("method2 running ...");
  9. }
  10. @Override
  11. public int method3(Integer i) {
  12. System.out.println("method3 running ...");
  13. return i;
  14. }
  15. }
  • Crear controlador de invocación:lograrInvocationHandlerinterfaz y reescribirinvoke método.existirinvokeEn el método, puede agregar lógica personalizada, como registro, verificación de permisos, etc., y llamar al método de clase original mediante reflexión.
  • Generar objeto proxy:transferirProxy.newProxyInstancemétodo, pasando el cargador de clases, la matriz de interfaz yInvocationHandler Instancia para generar dinámicamente objetos proxy. Este método devuelve una instancia de clase proxy que implementa la interfaz especificada.
  1. public class TargetProxy {
  2. public static <T> Object getTarget(T t) {
  3. //新构建了一个 新的 代理类的对象
  4. return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
  5. @Override
  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  7. // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
  8. //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
  9. System.out.println("执行方法前...");
  10. Object invoke = method.invoke(t, args);
  11. System.out.println("执行方法后...");
  12. return invoke;
  13. }
  14. });
  15. }
  16. }
  • Usar objetos proxy: Cuando se llama a un método a través de un objeto proxy, en realidad se llamaInvocationHandlerdeinvokeMétodo, después de ejecutar la lógica personalizada en este método, llame al método de la clase original.
  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
  4. target.method1();
  5. System.out.println("-----------------------------");
  6. target.method2();
  7. System.out.println("-----------------------------");
  8. System.out.println(target.method3(3));
  9. }
  10. }

Salida de resultados:
Antes de ejecutar el método...
método1 corriendo...
Después de ejecutar el método...
-----------------------------
Antes de ejecutar el método...
método2 ejecutándose...
Después de ejecutar el método...
-----------------------------
Antes de ejecutar el método...
método3 corriendo...
Después de ejecutar el método...
3

③ Características del proxy JDK

  1. Proxy de interfaz: JDK Proxy solo puede representar clases que implementan interfaces y no puede representar clases ordinarias que no implementan interfaces.
  2. Generado dinámicamente: La clase de proxy se genera dinámicamente en tiempo de ejecución y los desarrolladores no necesitan escribir manualmente el código de la clase de proxy.
  3. Alta flexibilidad: Se puede agregar funcionalidad o lógica adicional a la clase original sin modificar el código de clase original.
  4. Consideraciones de rendimiento: Debido a la participación de la reflexión y la generación dinámica de clases, el rendimiento de JDK Proxy puede ser ligeramente inferior al de los servidores proxy estáticos o las llamadas directas a métodos de la clase original.

2) CGLib

① El principio básico del proxy dinámico CGLib

  1. Operaciones de código de bytes : CGLib utiliza ASM (un marco de manipulación de código de bytes pequeño y rápido) internamente para generar dinámicamente nuevas clases Java (generalmente subclases de la clase de destino). Estas clases recién generadas heredan de la clase de destino e insertan lógica de proxy cuando se llama al método.
  2. Intercepción del método : La función principal de CGLib es implementar la interceptación a nivel de método.Los desarrolladores implementanMethodInterceptorInterfaz para definir un interceptor de métodos, que ejecutará lógica personalizada antes y después de la llamada al método del objeto proxy, como preprocesamiento, posprocesamiento, manejo de excepciones, etc.
  3. Mecanismo FastClass : Para mejorar el rendimiento, CGLib adopta el mecanismo FastClass. FastClass indexa los métodos de la clase de destino y accede al método de destino directamente a través del índice cuando se llama. Este método es mucho más rápido que la reflexión de Java.

② Pasos de implementación del proxy dinámico CGLib

  • Introduciendo las dependencias de CGLib:Introduzca la dependencia Maven o Gradle de CGLib en el proyecto.
  1. import net.sf.cglib.proxy.Enhancer;
  2. import net.sf.cglib.proxy.MethodInterceptor;
  3. import net.sf.cglib.proxy.MethodProxy;
  4. import java.lang.reflect.Method;
  • Definir clase objetivo: Defina la clase de destino que debe ser representada.
  1. public class Target {
  2. public void method1() {
  3. System.out.println("method1 running ...");
  4. }
  5. public void method2() {
  6. System.out.println("method2 running ...");
  7. }
  8. public int method3(Integer i) {
  9. System.out.println("method3 running ...");
  10. return i;
  11. }
  12. }
  • Implementar la interfaz MethodInterceptor:Crear una implementaciónMethodInterceptorclase de interfaz y anulaciónintercept método. Escriba la lógica del proxy en este método.
  • Crear objeto proxy: Utilice el proporcionado por CGLibEnhancer clase para crear objetos proxy.Debe configurar la clase de proxy (a través desetSuperclassmétodo) y devoluciones de llamada (a través desetCallbackEl método establece la clase de implementación MethodInterceptor).
  1. public class TargetProxy {
  2. public static <T> Object getProxy(T t) {
  3. Enhancer en = new Enhancer(); //帮我们生成代理对象
  4. en.setSuperclass(t.getClass());//设置要代理的目标类
  5. en.setCallback(new MethodInterceptor() {//代理要做什么
  6. @Override
  7. public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  8. System.out.println("执行方法前。。。");
  9. //调用原有方法
  10. Object invoke = methodProxy.invokeSuper(object, args);
  11. // Object invoke = method.invoke(t, args);// 作用等同与上面。
  12. System.out.println("执行方法后。。。");
  13. return invoke;
  14. }
  15. });
  16. return en.create();
  17. }
  18. }
  • Usar objetos proxy: Se activa cuando se llama al método de la clase de destino a través del objeto proxyinterceptLógica proxy en métodos.
  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. Target target = (Target) TargetProxy.getProxy(new Target());
  4. System.out.println(target.getClass().getName());
  5. target.method1();
  6. }
  7. }

Salida de resultados:

com.heaboy.aopdemo.cglibproxy.ObjetivominorteorteyoanorteorteCmiaByCGRAMOyoIBf9f41fb8
antes de ejecutar el método. . .
método1 corriendo...
Después de ejecutar el método. . .

③ Escenarios aplicables del proxy dinámico CGLib

  1. Necesidad de proxy de clases que no implementan interfaces: Cuando la clase de destino no implementa ninguna interfaz, CGLib se puede utilizar como proxy.
  2. Requisitos de alto rendimiento: En escenarios con requisitos de alto rendimiento, si el proxy dinámico JDK no puede satisfacer las necesidades, puede considerar usar CGLib.
  3. Implementación del marco AOP: En marcos de programación orientados a aspectos, como Spring AOP, cuando una clase que no implementa una interfaz necesita ser proxy, generalmente se usa CGLib como implementación subyacente.

④ Ventajas y desventajas del proxy dinámico CGLib

ventaja:

  1. Alta flexibilidad: Puede proxy clases que no implementan interfaces, ampliando el alcance de aplicación del proxy.
  2. Mejor interpretación: A través del mecanismo FastClass, la eficiencia de las llamadas es mayor que el mecanismo de reflexión del proxy dinámico JDK.
  3. Poderoso: Admite agregar dinámicamente funcionalidad o lógica adicional a las clases de destino en tiempo de ejecución sin modificar el código de clase original.

defecto:

  1. Sobrecarga de operación de código de bytes: Generar código de bytes dinámicamente y cargarlo en la JVM generará cierta sobrecarga de rendimiento.
  2. No se pueden representar clases y métodos finales: Dado que CGLib implementa el proxy heredando la clase de destino, no puede representar clases y métodos modificados finalmente.
  3. Más complejo de usar: En comparación con el proxy dinámico JDK, CGLib es más complejo de usar y necesita introducir dependencias adicionales y abordar problemas de generación de códigos de bytes.

3) Proxy JDK frente a CGLib

Tipo de objeto proxy:JDK Proxy solo puede representar clases que implementan interfaces, mientras que CGLib puede representar directamente clases ordinarias.

actuación: CGLib genera subclases de clases de proxy en tiempo de ejecución y generalmente se considera que tiene un rendimiento ligeramente mejor que JDK Proxy. Pero en la mayoría de los escenarios, esta diferencia de rendimiento no es significativa.

escenas a utilizar: Si el objeto de destino ya implementa la interfaz, usar JDK Proxy es una opción simple y directa. Si necesita representar una clase que no implementa una interfaz, debe usar CGLib.

confiar:JDK Proxy no requiere dependencias adicionales porque es parte de la biblioteca principal de Java, mientras que CGLib requiere agregar la biblioteca CGLib como dependencia del proyecto.

JDK Proxy es una función que viene con el lenguaje Java y no necesita implementarse cargando clases de terceros;

Java proporciona soporte estable para JDK Proxy y continuará actualizándose y actualizándose. Por ejemplo, el rendimiento de JDK Proxy en la versión Java 8 ha mejorado enormemente en comparación con las versiones anteriores;

JDK Proxy se implementa mediante interceptores y reflexión;

JDK Proxy solo puede representar clases que hereden interfaces;

JDK Proxy es relativamente sencillo de implementar y llamar;

CGLib es una herramienta proporcionada por un tercero, implementada en base a ASM y tiene un rendimiento relativamente alto;

CGLib no necesita implementarse a través de una interfaz, se llama implementando una subclase.

4. Proxy estático VS proxy dinámico

la diferencia

Proxy estático: Los servidores proxy estáticos se determinan durante la compilación. Es necesario escribir una clase de proxy para cada clase de proxy. La clase de proxy y la clase de proxy implementan la misma interfaz o heredan la misma clase principal.

La clase de proxy de un proxy estático existe en el momento de la compilación, por lo que solo puede representar clases específicas cuando el programa se está ejecutando y no puede decidir dinámicamente qué clases representar.

El proxy estático envuelve la llamada al método del objeto original y puede agregar lógica adicional antes y después de la llamada, pero la clase de proxy debe escribirse con anticipación, lo que aumentará la cantidad de código.

El proxy estático especifica explícitamente el objeto proxy en el código y su uso es relativamente intuitivo, pero agregar nuevas clases de proxy requiere una recompilación.

Proxy dinámico: El proxy dinámico crea objetos proxy en tiempo de ejecución sin escribir clases de proxy por adelantado. Utilice el mecanismo de reflexión de Java para generar dinámicamente clases proxy y objetos proxy.

Los proxies dinámicos se basan en interfaces y se implementan a través de la clase java.lang.reflect.Proxy y la interfaz java.lang.reflect.InvocationHandler.

El proxy dinámico puede representar clases de múltiples interfaces y decidir dinámicamente qué clases representar. En tiempo de ejecución, se pueden generar servidores proxy para diferentes objetos según sea necesario, lo que proporciona una mayor flexibilidad.

El proxy dinámico no necesita escribir una clase de proxy específica para cada clase de proxy, lo que es más flexible y ahorra código.

Los agentes dinámicos pueden agregar lógica personalizada antes y después de las llamadas a métodos en el objeto proxy, como registros, gestión de transacciones, etc. La desventaja del proxy dinámico es que, en comparación con el proxy estático, generar objetos proxy en tiempo de ejecución requiere cierta sobrecarga de rendimiento.

Escena aplicable

Los proxies estáticos son adecuados para los siguientes escenarios:

Cuando la cantidad de objetos de destino (objetos proxy) es limitada y determinada, el proxy estático se puede implementar escribiendo clases de proxy manualmente. Los proxies estáticos crean clases de proxy en tiempo de compilación, por lo que tienen un mejor rendimiento en tiempo de ejecución.

Los proxies estáticos encapsulan el objeto de destino y agregan funciones adicionales sin modificar el código original. Esto hace que los servidores proxy estáticos se utilicen a menudo para cuestiones transversales, como el registro y la gestión de transacciones.

El proxy dinámico es adecuado para los siguientes escenarios:

Cuando el número de objetos de destino es incierto o no se puede determinar de antemano, los servidores proxy dinámicos pueden generar objetos proxy de manera más conveniente. Genera clases de proxy y objetos de proxy en tiempo de ejecución, evitando el tedioso trabajo de escribir manualmente varias clases de proxy.

Los proxies dinámicos brindan la flexibilidad de agregar, eliminar o cambiar el comportamiento del proxy para los objetos de destino en tiempo de ejecución. Esto hace que los servidores proxy dinámicos se utilicen a menudo en escenarios de aplicaciones como AOP (programación orientada a aspectos) y RPC (llamada a procedimiento remoto).

Cabe señalar que debido a que los servidores proxy dinámicos crean clases de proxy y objetos proxy a través del mecanismo de reflexión en tiempo de ejecución, su rendimiento puede ser ligeramente menor que el de los servidores proxy estáticos. Además, los proxies dinámicos solo pueden apuntar a objetos que implementan la interfaz, mientras que los proxies estáticos no tienen esta restricción.

En resumen, los servidores proxy estáticos son adecuados para escenarios donde el número de objetos de destino es limitado y seguro, y requieren encapsulación y funciones adicionales, mientras que los servidores proxy dinámicos son adecuados para escenarios donde el número de objetos de destino es incierto o no se puede determinar de antemano; y el comportamiento del proxy debe agregarse, eliminarse o cambiarse de manera flexible. Elija el método de agencia adecuado según las necesidades y circunstancias específicas.

5. Implementación de proxy en SpringAOP

1) Introducción a SpringAOP

Hablar sobre la comprensión de AOP.

Spring AOP es un módulo importante en el marco Spring y se utiliza para implementar programación orientada a aspectos.

Programación cara a cara , este es un modelo de programación que permite a los programadores modularizar a través de puntos transversales personalizados y encapsular comportamientos que afectan múltiples clases en módulos reutilizables. Ejemplo: por ejemplo, la salida del registro, si no usa AOP, debe colocar las declaraciones de salida del registro en todas las clases y métodos. Sin embargo, con AOP, puede encapsular las declaraciones de salida del registro en un módulo reutilizable y colocarlas en un. De manera declarativa. En una clase, la salida del registro se completa automáticamente cada vez que se utiliza la clase.

En la idea de programación orientada a aspectos, las funciones se dividen en dos tipos

  • Negocio principal: El inicio de sesión, el registro, la adición, la eliminación, la modificación y la consulta se denominan negocio principal

  • Funciones periféricas: Los registros y la gestión de transacciones son servicios periféricos secundarios.

En la programación orientada a aspectos, las funciones comerciales centrales y las funciones periféricas se desarrollan de forma independiente, y las dos no están acopladas. Luego, las funciones comerciales centrales y las funciones comerciales centrales se "tejen", lo que se denomina AOP.

AOP puede convertir aquellos que no estén relacionados con el negocio,Pero está encapsulado para la lógica o responsabilidades (como procesamiento de transacciones, gestión de registros, control de permisos, etc.) comúnmente llamadas por módulos comerciales.,facil deReducir el código duplicado en el sistema.Reducir el acoplamiento entre módulos.,yPropicio para la futura escalabilidad y mantenibilidad

Existen los siguientes conceptos en AOP:

  • Aspecto J: Aspecto es solo un concepto. No existe una interfaz o clase específica correspondiente. Es un nombre colectivo para Punto de unión, Consejo y Punto de corte.

  • Punto de unión : El punto de conexión se refiere a un punto durante la ejecución del programa, como la invocación de métodos, el manejo de excepciones, etc. En Spring AOP, solo se admiten puntos de unión a nivel de método.

  • Consejo : La notificación, es decir, la lógica transversal en un aspecto que definimos, tiene tres tipos: "alrededor", "antes" y "después". En muchos marcos de implementación de AOP, Advice generalmente actúa como un interceptor y también puede contener muchos interceptores como un enlace que se procesará alrededor del punto de unión.

  • Punto de corte: Pointcut, utilizado para unir puntos de unión. Los puntos de unión contenidos en un AspectJ deben filtrarse mediante Pointcut.

  • Introducción : Introducción, que permite que un aspecto declare que los objetos recomendados implementan cualquier interfaz adicional que en realidad no implementan. Por ejemplo, un objeto proxy se puede utilizar para representar dos clases de destino.

  • Costura : Weaving, ahora que tenemos puntos de conexión, puntos de corte, notificaciones y aspectos, ¿cómo aplicarlos al programa? Así es, está tejido. Bajo la guía de puntos de corte, la lógica de notificación se inserta en el método de destino, de modo que nuestra lógica de notificación se pueda ejecutar cuando se llama al método.

  • Proxy AOP : El proxy AOP se refiere al objeto que implementa el protocolo de aspecto en el marco de implementación de AOP. Hay dos tipos de proxies en Spring AOP, a saber, el proxy dinámico JDK y el proxy dinámico CGLIB.

  • Objeto objetivo: El objeto de destino es el objeto que se está representando.

Spring AOP se implementa en base al proxy dinámico JDK y la promoción Cglib. Ambos métodos de proxy son métodos de tiempo de ejecución, por lo que no tienen procesamiento en tiempo de compilación, por lo que Spring se implementa mediante código Java.

¿Qué problema resuelve AOP?

Algunos comportamientos comunes dispersos entre múltiples clases u objetos (como registro, administración de transacciones, control de permisos, limitación de corriente de la interfaz, potencia de la interfaz, etc.), estos comportamientos generalmente se denominan preocupaciones transversales . Si implementamos estos comportamientos repetidamente en cada clase u objeto, generaremos un código redundante, complejo y difícil de mantener.

AOP puede transformar preocupaciones transversales (como registro, gestión de transacciones, control de permisos, limitación de corriente de interfaz, potencia de interfaz, etc.) de Lógica empresarial central (preocupaciones centrales, preocupaciones centrales) Separarse del foco para lograr la separación de preocupaciones.

Escenarios de aplicación AOP

  • Registro: personalice las anotaciones de registro y utilice AOP para lograr el registro con una línea de código.

  • Estadísticas de rendimiento: utilice AOP para contar el tiempo de ejecución del método antes y después de la ejecución del método de destino para facilitar la optimización y el análisis.

  • Gestión de transacciones:@Transactional Las anotaciones permiten que Spring realice la gestión de transacciones por nosotros, como revertir operaciones de excepción y eliminar la lógica de gestión de transacciones repetidas.@TransactionalLas anotaciones se implementan en función de AOP.

  • Control de permisos: utilice AOP para determinar si el usuario tiene los permisos necesarios antes de ejecutar el método de destino. Si es así, ejecute el método de destino; de lo contrario, no se ejecutará.Por ejemplo, SpringSecurity utiliza@PreAuthorize Puede personalizar la verificación de permisos comentando una línea de código.

  • Limitación de corriente de la interfaz: utilice AOP para limitar la solicitud a través de implementaciones y algoritmos de limitación de corriente específicos antes de que se ejecute el método de destino.

  • Gestión de caché: utilice AOP para leer y actualizar el caché antes y después de que se ejecute el método de destino.

Cómo se implementa AOP

Los métodos de implementación comunes de AOP incluyen proxy dinámico, operación de código de bytes, etc.

Spring AOP se basa en un proxy dinámico. Si el objeto a ser proxy implementa una determinada interfaz, entonces Spring AOP utilizará. Proxy JDKPara crear un objeto proxy, para objetos que no implementan la interfaz, no puede usar JDK Proxy como proxy. En este momento, Spring AOP usará. Cglib Genere una subclase del objeto proxy para que sirva como proxy

2) Implementar SpringAOP basado en el proxy dinámico JDK Proxy

① Configurar SpringAOP

existirspring-aop.xmlConfigurar beans y aspectos relacionados en el archivo de configuración

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  5. <bean id="target" class="com.xxhh.aopdemo.aop.Target"/>
  6. <bean id="targetAdvice" class="com.xxhh.aopdemo.aop.TargetAdvice"/>
  7. <bean id="targetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  8. <property name="target" ref="target"/> <!--被代理的类-->
  9. <property name="interceptorNames" value="targetAdvice"/> <!--如果用多种增强方式,value的值使用逗号(,)分割-->
  10. <property name="proxyTargetClass" value="false"/> <!--如果设置为true,则创建基于类的代理(使用CGLIB);如果设置为false,则创建基于接口的代理(使用JDK动态代理)。-->
  11. <property name="interfaces" value="com.xxhh.aopdemo.aop.TargetInteface"/> <!--target实现的接口-->
  12. </bean>
  13. </beans>

② Definir interfaz abstracta

  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }

③ Definir la clase de proxy

  1. public class Target implements TargetInteface{
  2. /*
  3. * 需要增强的方法,连接点JoinPoint
  4. **/
  5. @Override
  6. public void method1() {
  7. System.out.println("method1 running ...");
  8. }
  9. @Override
  10. public void method2() {
  11. System.out.println("method2 running ...");
  12. }
  13. @Override
  14. public int method3(Integer i) {
  15. System.out.println("method3 running ...");
  16. return i;
  17. }
  18. }

④ Definir clase de proxy (método de mejora)

  1. public class TargetAdvice implements MethodInterceptor, MethodBeforeAdvice, AfterReturningAdvice {
  2. /*
  3. * 通知/增强
  4. **/
  5. @Override
  6. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  7. System.out.println("前置环绕通知");
  8. Object proceed = methodInvocation.proceed();
  9. System.out.println("后置环绕通知");
  10. return proceed;
  11. }
  12. @Override
  13. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  14. System.out.println("后置返回通知");
  15. }
  16. @Override
  17. public void before(Method method, Object[] args, Object target) throws Throwable {
  18. System.out.println("前置通知");
  19. }
  20. }

⑤ Prueba

  1. public class AopTest {
  2. public static void main(String[] args) {
  3. ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-aop.xml");
  4. TargetInteface targetProxy = (TargetInteface) appCtx.getBean("targetProxy");
  5. targetProxy.method1();
  6. }
  7. }

Resultado de salida:

Notificación frontal envolvente
Notificación previa
método1 corriendo...
notificación de devolución posterior
Notificación envolvente trasera

3) Implementar SpringAOP basado en el proxy dinámico CGLib

① Configurar SpringAOP

existirspring-confaop.xmlConfigurar beans y aspectos relacionados en el archivo de configuración

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  7. <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
  8. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
  9. <!--扫包-->
  10. <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
  11. </beans>

② Definir la clase de proxy

  1. /*
  2. * 目标类
  3. **/
  4. public class Target {
  5. public void method1() {
  6. System.out.println("method1 running ...");
  7. }
  8. public void method2() {
  9. System.out.println("method2 running ...");
  10. }
  11. /*
  12. * 连接点JoinPoint
  13. **/
  14. public int method3(Integer i) {
  15. System.out.println("method3 running ...");
  16. // int i1 = 1 / i;
  17. return i;
  18. }
  19. }

③ Definir clase de proxy (clase de aspecto)

  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. /*
  3. * 切面类
  4. **/
  5. public class TargetAspect {
  6. /*
  7. * 前置通知
  8. **/
  9. public void before() {
  10. System.out.println("conf前置通知");
  11. }
  12. public void after() {
  13. System.out.println("conf后置通知");
  14. }
  15. public void afterReturning() {
  16. System.out.println("conf后置返回通知");
  17. }
  18. public void afterThrowing(Exception ex) throws Exception {
  19. // System.out.println("conf异常通知");
  20. // System.out.println(ex.getMessage());
  21. }
  22. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  23. Object proceed = null;
  24. if (!"".equals("admin")) {
  25. System.out.println("conf环绕前置");
  26. proceed = pjp.proceed(pjp.getArgs());
  27. System.out.println("conf环绕后置");
  28. }
  29. return proceed;
  30. }
  31. }

④ Prueba

  1. public class AopTest {
  2. public static void main(String[] args) {
  3. ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-confaop.xml");
  4. Target targetProxy = (Target) appCtx.getBean("target");
  5. System.out.println(targetProxy.method3(0));
  6. }
  7. }

Resultado de salida:

notificación previa de configuración
prefijo envolvente conf
método3 corriendo...
conf notificación posterior a la devolución
publicación envolvente de configuración
notificación de publicación de configuración

4) Implementar SpringAOP basado en proxy dinámico de anotaciones

① Configurar SpringAOP

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  7. <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
  8. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
  9. <!--扫包-->
  10. <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
  11. </beans>

② Definir anotaciones

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface TestAnnotation{
  5. }

③ Definir clases de aspectos

  1. /*
  2. * 切面类
  3. **/
  4. @Aspect
  5. @Component
  6. public class AnnotationAspect {
  7. // 定义一个切点:所有被RequestMapping注解修饰的方法会织入advice
  8. @Pointcut("@annotation(TestAnnotation)")
  9. private void advicePointcut() {}
  10. /*
  11. * 前置通知
  12. **/
  13. @Before("advicePointcut()")
  14. public void before() {
  15. System.out.println("annotation前置通知");
  16. }
  17. @After("advicePointcut()")
  18. public void after() {
  19. System.out.println("annotation后置通知");
  20. }
  21. @AfterReturning(pointcut = "advicePointcut()")
  22. public void afterReturning() {
  23. System.out.println("annotation后置返回通知");
  24. }
  25. @AfterThrowing(pointcut = "advicePointcut()", throwing = "ex")
  26. public void afterThrowing(Exception ex) throws Exception {
  27. System.out.println("annotation异常通知");
  28. System.out.println(ex.getMessage());
  29. }
  30. @Around("advicePointcut()")
  31. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  32. Object proceed = null;
  33. if (!"".equals("admin")) {
  34. System.out.println("annotation环绕前置");
  35. proceed = pjp.proceed(pjp.getArgs());
  36. System.out.println("annotation环绕后置");
  37. }
  38. return proceed;
  39. }
  40. }

④ El controlador agrega anotaciones

  1. @Controller
  2. public class TestController {
  3. @RequestMapping("/test.do")
  4. @ResponseBody
  5. public String testController() {
  6. TestController o = (TestController) AopContext.currentProxy();
  7. o.test();
  8. // System.out.println("tewt");
  9. return "ok";
  10. }
  11. @TestAnnotation
  12. public void test() {
  13. System.out.println("test running");
  14. }
  15. }

⑤ Prueba