Spring-AOP: preparación para el desarrollo, proxy dinámico, pasos de uso,

Escena Aop

Construimos una escena de calculadora para aprender

Interfaz de calculadora (calculadora)

package com.jiang.inter;

/**
 * @Title:
 * @author: JiangPeng
 * @Code: No Bug
 */
public interface Calculator {
    public int add(int a,int b);
    public int del(int a,int b);
    public int mul(int a,int b);
    public int div(int a,int b);
}

Clase de implementación MyCalculator

package com.jiang.impl;

import com.jiang.inter.Calculator;

/**
 * @Title:
 * @author: JiangPeng
 * @Code: No Bug
 */
public class MyCalculator implements Calculator {

    public int add(int a, int b) {
        int result = a + b;
        return result;
    }

    public int del(int a, int b) {
        int result = a - b;
        return result;
    }

    public int mul(int a, int b) {
        int result = a * b;
        return result;
    }

    public int div(int a, int b) {
        int result = a / b;
        return result;
    }
}

Prueba de funcionamiento Test01

public class Test01 {
    public static void main(String[] args) {
        Calculator calculator = new MyCalculator();
        System.out.println(calculator.add(1,2););
    }
}

Obviamente, nuestro resultado en este momento es generar un valor de 1 + 2, que es 3

1. Aumentar el registro

Luego, necesitamos agregar una función de registro en este momento: es decir, agregar un registro antes y después del resultado de la ejecución del método para solicitarnos que ingresemos el valor y el resultado

Método de registro de escritura interna

public class MyCalculator implements Calculator {

    public int add(int a, int b) {
        System.out.println("【add】方法开始了,它的参数值是【"+a+"和"+b+"】");
        int result = a + b;
        System.out.println("【add】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }

    public int del(int a, int b) {
        System.out.println("【del】方法开始了,它的参数值是【"+a+"和"+b+"】");
        int result = a - b;
        System.out.println("【del】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }

    public int mul(int a, int b) {
        System.out.println("【mul】方法开始了,它的参数值是【"+a+"和"+b+"】");
        int result = a * b;
        System.out.println("【mul】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }

    public int div(int a, int b) {
        System.out.println("【div】方法开始了,它的参数值是【"+a+"和"+b+"】");
        int result = a / b;
        System.out.println("【div】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }
}

En este punto ejecutamos Test para ver los resultados.

[Agregar] El método comienza, sus valores de parámetro son [1 y 2]
[agregar] El método se ejecuta hasta el final, su resultado es [3]

Tenemos impresiones de registro antes y después de los resultados;

Pero si necesitamos hacer una modificación de registro en este momento, cambiar [] a [], ¿necesitamos modificarla una por una, el volumen de negocios parece estar bien, pero si estamos en desarrollo empresarial, negocios múltiples? Si se utiliza la salida del registro, será particularmente problemático modificarlo, por lo que definitivamente no recomendamos esta forma de escritura.

Este tipo de registro se escribe en el código, y cuando necesitamos cambiar el código fuente de un enlace en el registro de registro, esto se llama acoplamiento.

Extraer en herramientas de registro

Extraer registros en LogUtil

package com.jiang.utils;

import java.util.Arrays;

/**
 * @Title:
 * @author: JiangPeng
 */
public class LogUtil {
    public static void logStart(Object...objects){
        System.out.println("【xx】方法开始了,它的参数值是【"+ Arrays.asList(objects) +"】");
    }
}

Clase de implementación en este momento

public class MyCalculator implements Calculator {

    public int add(int a, int b) {
        LogUtil.logStart(a,b);
        int result = a + b;
        System.out.println("【add】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }

    public int del(int a, int b) {
        LogUtil.logStart(a,b);
        int result = a - b;
        System.out.println("【del】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }

    public int mul(int a, int b) {
        LogUtil.logStart(a,b);
        int result = a * b;
        System.out.println("【mul】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }

    public int div(int a, int b) {
        LogUtil.logStart(a,b);
        int result = a / b;
        System.out.println("【div】方法运行结束了,它的结果是【"+result+"】");
        return result;
    }
}

Cuando ejecutamos el resultado:

[Xx] El método comienza y su valor de parámetro es [[1, 2]]
[add] El método está terminado, su resultado es [3]

Como puede ver, no podemos obtener el nombre específico del método, por lo que este problema de compatibilidad de métodos debe ser considerado. De hecho, simplemente reemplazamos el método de System.out.println con el método de llamar a LogUtil, por lo que todavía está acoplado.


2. Agente dinámico

Esperamos que el módulo de registro se agregue dinámicamente durante la operación de las funciones principales sin modificar el código de lógica de negocios.

Implementación de recuperación de código de clase

public class MyCalculator implements Calculator {

    public int add(int a, int b) {
        int result = a + b;
        return result;
    }

    public int del(int a, int b) {
        int result = a - b;
        return result;
    }

    public int mul(int a, int b) {
        int result = a * b;
        return result;
    }

    public int div(int a, int b) {
        int result = a / b;
        return result;
    }
}

Podemos ver que cuando ejecutamos el código comercial, el objeto mismo llama directamente al código comercial.

Esperamos que haya un objeto proxy que pueda ejecutar indirectamente nuestro código comercial para nosotros, pero podemos hacer algo más antes y después de la ejecución

Calculator proxy = new CalculatorPorxy.getProxy(calculator);
proxy.add(2,1);

Cuando proxy.add es realmente calculator.add llamado por proxy, entonces no podemos modificar el código comercial original de la calculadora, pero agregamos la función de generar registros de registro en proxy

//本来是proxy.add => proxy.calculator.add
proxy{
    System.....("日志....结果"+a+"..");
    calculator{
        add();
        del();
    }
    System.....("日志....结束"+result+"..");
}

Calculator Porxy clase proxy dinámico

  1. Valor de parámetro del método getProxy Calculator calculator: objeto proxy entrante
  2. Creamos un objeto proxy a través de Proxy.newProxyInstance () de JDK
  3. Proxy.newProxyInstance (classLoader, classes, invocationHandler); después de la creación, los tres parámetros son
  4. classLoader: el cargador de clases del objeto proxy
  5. clases: todas las interfaces implementadas por el objeto proxy
  6. invocationHandler: ejecutor de métodos, ayuda a nuestro objeto de destino a ejecutar el método de destino para usar nuevo y luego tener una implementación anónima
  7. Invocación de implementación anónima, valor de parámetro Proxy de objeto: objeto proxy: para uso de JDK, no tocamos este objeto en ningún momento
  8. Realización anónima de invocación, valor del parámetro Método método: el método del objeto de destino que se ejecutará actualmente.
  9. Implementación anónima de invocación, valor de parámetro Object [] args: el valor del parámetro pasado desde afuera cuando se llama a este método
  10. Dentro de la invocación, necesitamos usar la reflexión para ejecutar el método de destino, method.invoke (calculadora, argumentos); valor de retorno Resultado del objeto
  11. calculadora: el objeto proxy que se ejecutará; si necesita usar un parámetro declarado en la clase interna, debe establecer este parámetro en final, es decir, el valor del parámetro en el método getProxy se convierte en getProxy (calculadora calculadora final)
  12. resultado: el valor de retorno después de ejecutar el método de destino. El valor de retorno debe devolverse antes de que el mundo exterior pueda obtener el valor de retorno después de la ejecución real
public class CalculatorPorxy {
    public static Calculator getProxy(final Calculator calculator){
        /*
           为传入的参数创建一个动态代理对象
           Calculator calculator:传入的被代理对象
           创建代理对象通过JDK的Proxy.newProxyInstance()
         */
        
        //被代理对象的类加载器
        ClassLoader classLoader = calculator.getClass().getClassLoader();
        
        //被代理对象所实现的所有接口
        Class<?>[] classes = calculator.getClass().getInterfaces();

        InvocationHandler invocationHandler = new InvocationHandler() {
            /*
                Object proxy:代理对象:给JDK使用,我们任何时候不要动这个对象
                Method method:当前将要执行的目标对象的方法。
                Object[] args:这个方法调用时外界传入的参数值
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //利用反射执行目标方法
                //calculator:执行的代理对象;在这里如果需要在内部类里用一个声明的参数,需要将这个参数设置final
                //result:目标方法执行后的返回值
                Object result = method.invoke(calculator, args);
                return result;//返回值必须返回出去,外界才能拿到真正执行后的返回值
            }
        };//方法执行器,帮我们目标对象执行目标方法

        //proxy为目标对象创建代理对象
        Object proxy = Proxy.newProxyInstance(classLoader, classes, invocationHandler);
        return (Calculator) proxy;
    }


}

Lo siguiente que se usa en el registro es agregar la declaración de registro de salida antes y después de que se llame a nuestro método de invocación

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    System.out.println("【"+method.getName()+"】方法执行开始,该方法的参数值是【"+ Arrays.asList(args)+"】");
    Object result = method.invoke(calculator, args);
    System.out.println("【"+method.getName()+"】方法执行结束了");
    
    return result;
}

method.getName: obtenga el nombre del método por reflexión

Arrays.asList (args): convierte los valores de parámetros externos en matrices

En este momento probamos como

public static void main(String[] args) {
    Calculator calculator = new MyCalculator();
    //calculator.add(1,2);

    Calculator proxy = CalculatorPorxy.getProxy(calculator);
    proxy.add(1,2);

}

En este momento, podemos implementar la función de registro sin modificar ningún código comercial

Ventajas: Podemos usar un agente dinámico para ejecutar dinámicamente el código de registro antes y después del método de destino;

Desventajas: 1. Es muy difícil escribir: en este momento, ya es muy problemático para nosotros realizar un proxy dinámico en una calculadora simple, sin mencionar que definitivamente es más complicado en el desarrollo real de lo que es ahora.

proxy dinámico predeterminado 2.jdk si el objeto no implementa ninguna interfaz, no puede crear objeto proxy para él. Porque lo único con lo que el objeto proxy y el objeto proxy pueden asociarse es la implementación de la misma interfaz
Class<?>[] classes = calculator.getClass().getInterfaces();

3. Generación Aop

Entonces, para resolver las deficiencias de las deficiencias de los proxys dinámicos, Spring implementó AOP. La capa inferior sigue siendo proxys dinámicos, ¡pero no hay un requisito obligatorio de que los objetos deben tener interfaces! Simplifica enormemente el uso de la dificultad y, en el futuro, con el proxy dinámico

Aop

image-20200410204944752

  • Preocupaciones transversales: cada método comienza con un registro. Lo miramos horizontalmente, llamado inquietudes transversales.
  • Método de notificación: transferimos un método para iniciar sesión antes del inicio de cada método, denominado método de notificación LogUtil.begin ();
  • Clase de aspecto: es la clase de herramienta del método de notificación: LogUtil
  • Punto de conexión: la posición de la cruz, cada posición de cada método es un punto de conexión, un total de 16 en la imagen de arriba
  • Punto de entrada: donde realmente se necesita iniciar sesión
  • Expresiones de puntos de corte: encuentre lugares de interés entre muchos puntos de conexión

Aop pasos

1. Paquete de guía

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

2. Configuración de escritura

1. Agregue la clase de destino y la clase de aspecto al contenedor ioc. Clase de aspecto (LogUtil, una clase que encapsula el método de notificación)

Agregue @service a MyCalculator y @Component a LogUtil

2. Dile a Spring que es una clase facetada (porque Spring no distingue la clasificación específica de tus anotaciones)

Nueva anotación @Aspect

3. Indique a Spring cuándo se ejecuta cada método en la clase de aspecto

@Antes: ejecutar antes del método de destino, notificación previa

@Después: después del final del método de destino, publique la notificación

@AfterReturning: después de que el método de destino vuelva normalmente, notificación de devolución

@AfterThrowing: se ejecuta después de que el método de destino arroje una excepción, notificación de excepción

@Around: envolvente, notificación envolvente

try{
    @Before
    method.invoke(obj,args);
	@AfterReturning
}catch(e){
    @AfterThrowing
}finally{
    @After
}

4. Active el AOP basado en anotaciones

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

Clase LogUtil

@Aspect
@Component
public class LogUtil {
    //想在执行目标之前运行,写入切入点表达式
    //execution(访问权限符 返回值类型 方法签名)
    @Before("execution(public int com.jiang.impl.MyCalculator.add(int,int))")
    public static void logStart(){
        System.out.println("【】方法开始前,参数值是【】");
    }
}

ejecución (public int com.jiang.impl.MyCalculator.add (int, int))

ejecución (firma de método de tipo de valor de retorno de carácter de permiso de acceso)

3. Realizar la prueba

Preste atención para obtener el objeto de destino del contenedor ioc, si desea obtenerlo por tipo, debe usar su tipo de interfaz, no use esta clase

public class Test {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

        Calculator bean = app.getBean(Calculator.class);
        bean.add(1,2);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.jiang"></context:component-scan>

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

Supongo que te gusta

Origin www.cnblogs.com/pengcode/p/12576009.html
Recomendado
Clasificación