SpringAOP avanzado (ejemplo de vida -> modo proxy -> análisis de código fuente)

        Después de la fundación de los dos primeros artículos, finalmente llegó al aprendizaje del conocimiento de Spring AOP

        Antes de hablar sobre AOP, hablemos sobre el patrón de diseño utilizado en él: el patrón de diseño de proxy

¿Qué es un agente? ¿Por qué debería haber un agente?

Pequeños ejemplos de vida:

现在很多公寓式租房就是采用代理模式

角色:

房东、租房代理、房客

角色需求:

房东:需要房客入租客房,只想签合同收钱,但房源信息的宣传,打广告等额外的工作较为繁杂

房客:需要了解房源信息,以确保租房体验得到保证

矛盾:房东不想要宣传和打广告这些繁杂的工作,他只希望房客直接来跟他交钱签合同,然后入住就ok了,但是,如果没有宣传和广告这部分工作就会导致房客不能看到相应的房源信息,得不到保障,从而不敢或者不愿意去租房

解决问题者:租房代理

租房代理责任:代替房东,对房源信息进行宣传和做广告,包括一切帮助房客了解房源的工作



最后再来梳理一下每个角色的需求和责任:

房东:负责入住合同的签署和收取房费

代理:负责对房源信息进行宣传和帮助房客了解房源,大型代理还可以帮助房东管理房客入住后的所有事情(合同收钱除外)

房客:通过房源信息广告和实地了解后跟房东签署入住合同

这样上面的矛盾就得到了解决,并且房东和房客都不会有太多的顾虑和多余的繁杂事儿

análisis:

De acuerdo con los ejemplos vivos anteriores, la agencia de alquiler debe llamar al método del propietario para firmar el contrato. Según este requisito, nuestro objeto proxy y el objeto proxy (objeto de destino) deben implementar la misma interfaz para garantizar que el objeto de proxy pueda tener el objeto de proxy (objetivo Object), llame al método de firma de contrato del objeto proxy (objeto de destino) en el método de firma de contrato del objeto proxy

A partir de los pequeños ejemplos de vida anteriores, podemos ver la importancia de la agencia y el significado de su existencia —————— Resolver la contradicción entre el propietario y el inquilino debido a la información de la vivienda

Puede ser un poco confuso entender las palabras anteriores, ahora lo implementaremos en el código Java para ver

El desarrollo de JEE se divide principalmente en tres capas: Controlador ——> Servicio ——> Dao

La capa de servicio es la capa más importante, porque nuestro código comercial está escrito en esta capa, echemos un vistazo a qué código contiene la capa de servicio.

Capa de servicio  = función principal (código comercial ) + función adicional ( función adicional )
 
1. Función principal
        Computación empresarial
        Llamada DAO
2. Funciones adicionales
        1. No es un negocio
       2 disponibles
       3. La cantidad de código es muy pequeña
 
      Cuáles son las funciones adicionales: transacción, registro, rendimiento ...
 

En este momento, hay un problema. ¿Podemos escribir directamente el código de función adicional en la capa de servicio? ¿Por qué es necesario mencionarlo como una pieza separada?

análisis:

Persona que llama al servicio: espero que las funciones adicionales estén escritas en la capa de servicio ———————— Invitado

Diseñador de software: espero que la capa de servicio solo escriba código comercial y no requiera código funcional adicional para confundir a la empresa ———————— El propietario

¡Entonces habrá diferencias! ———— Esto es similar al conflicto entre el propietario y el inquilino debido a la información de la lista

Solución: ¡ Utilice el modo proxy!

Modelo de agencia

代理(Proxy)是一种设计模式,提供了对目标对象的另一种(非直接)访问方式

访问者通过代理对象来间接的访问目标对象

优点:实现目标对象业务功能的同时,能增加额外的功能和操作,即扩展目标对象

日常开发思想:不要随意去修改别人已经写好的代码或者方法,如需修改,则可以通过代理的方式来扩展该方法

代理模式的关键点:代理对象和目标对象,代理对象是对目标对象的扩展,并会调用目标对象

El concepto de modo proxy: a través de clases proxy, para aumentar las funciones adicionales de la clase original (objetivo)

Clase de destino, clase original: se refiere a la clase empresarial ( función principal -> operación empresarial llamada DAO )

Método de destino, método original: método de la clase de destino o clase original

Funciones adicionales, funciones adicionales: registro, transacción, rendimiento

Clase de proxy = clase de destino (clase original) + funciones adicionales + implementar la misma interfaz que la clase original (clase de destino)

#接口
public interface UserService{

        A(); //A方法
        B(); //B方法

}

#原始类(目标类)————房东

public class UserServiceImpl implements UserService{

        A(){} //A方法
        B(){} //B方法

}

#代理类(额外功能)————————租房代理

public class UserServiceProxy implements UserService{

        A(){} //A方法
        B(){} //B方法

}


Bueno, en este punto, el agente debe explicarse claramente desde la vida hasta el código, ¡así que ahora tenemos que entender el patrón de diseño del agente en profundidad!

Clasificación del modelo de agente

Proxy estático: para cada clase primitiva, escriba manualmente una clase de proxy (.java .class)

Proxy dinámico: no es necesario escribir clases de proxy manualmente para cada clase de proxy. La tecnología de código de bytes dinámico de terceros proporciona una plantilla de código de bytes .class para la clase de proxy.

扩展:

正常情况下我们创建一个类————在这里指的是代理类

1.从本地或网络加载.calss文件
2.根据.class文件在方法区中创建一个class字节码模板用于提供该类的模板信息(元信息)
3.根据class字节码模板在堆空间中创建一个java.lang.class对象
4.当我们需要实例对象的时候再通过class对象在堆空间中创建相应的实例对象


在这些步骤中,最重要的是我们必须要拿到.class字节码模板,
但是在动态代理中这个字节码模板是由第三方动态字节码技术来提供的——————具体是哪些第三方技术,请往下看!!!!!

¡Comprenda la teoría, luego explique, codifique! ! ! !

Proxy estático:

步骤:
1.提供业务接口
2.编写业务类(目标类)及方法
3.编写代理类,要求和目标类实现相同的接口,并调用目标类的方法
4.调用代理类的方法

1. Proporcionar una interfaz empresarial

public interface UserService {

    public void register();
}

2. Escriba la clase empresarial (clase objetivo) y los métodos

public class UserServiceImpl  implements UserService {
    @Override
    public void register() {
        System.out.println("我是业务代码!!!");
    }
}

3. Escriba la clase de proxy, requiera la misma interfaz que la clase de destino y llame al método de la clase de destino

public class UserServiceProxy implements UserService {

    private final UserServiceImpl userService = new UserServiceImpl();
    @Override
    public void register() {
        System.out.println("我是额外功能!!!");
        userService.register();
    }
}

4. Llame al método de la clase de proxy.

5. Resultados

Bueno, la teoría y la codificación se han realizado, el siguiente paso es analizar

análisis:

Proxy estático

     Ventajas: la clase empresarial se mejora sin modificar el código de la clase empresarial (clase objetivo)

     Desventajas: debido a que la clase de proxy necesita implementar la misma interfaz que la clase de destino, si hay más clases de negocio, habrá muchas clases de proxy. Al mismo tiempo, una vez que la interfaz agrega métodos, el objeto de destino y el objeto de proxy deben mantenerse

Entonces, ¿cómo resolver el problema causado por el proxy estático? !

¡En ese momento, el agente dinámico de Spring conducía su Maserati Benz!

 

Primero echemos un vistazo a cómo usar el desarrollo de proxy dinámico de Spring:

Proxy dinámico:

Spring动态代理编程步骤:
1.引入依赖
2.创建接口和目标类(业务类)
3.编写额外功能类
4.定义切入点
5.组装切面(组装步骤3和步骤4)
6.调用测试
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.1.14.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.8</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.3</version>
</dependency>
#准备接口和目标类(业务类)

public interface OrderService {

    void productOder();

    Boolean consumOrder();

}

public class OrderServiceImpl implements OrderService {
    @Override
    public void productOder() {
        System.out.println("下单");
    }

    @Override
    public Boolean consumOrder() {
        System.out.println("消费订单");
        return true;
    }
}

#准备额外功能类

public class OrderExtra1 implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("我是订单需要的额外功能!!!");
    }
}

#在xml中将目标类和额外功能类交给Spring去管理

<bean id="orderSevice" class="com.xiaozhao.spring.service.impl.OrderServiceImpl"/>

<bean id="orderExtra" class="com.xiaozhao.spring.service.extra.orderExtra1"/>

#设置切入点和组装切面

<aop:config>
   <aop:pointcut id="extra" expression="execution(* *(..))"/> //切入点
   <aop:advisor advice-ref="orderExtra" pointcut-ref="extra"/> //组装
</aop:config>
测试:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");

OrderService orderSevice = (OrderService) ctx.getBean("orderSevice");
orderSevice.productOder();
orderSevice.consumOrder();

análisis:

public class orderExtra1 implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("我是订单需要的额外功能!!!");
    }
}

A partir de los resultados, podemos ver el rol de la interfaz MethodBeforeAdvice:

Análisis de parámetros:

Método método: método objetivo (método en la clase objetivo)

Objeto [] objetos: parámetros del método de destino

Objeto o: clase de destino (objeto de destino)

Escriba la función adicional en el método before de la clase de implementación de la interfaz MethodBeforeAdvice, y opere antes de que se ejecute el método original, y luego mejore el método de destino, pero este método tiene el inconveniente de que solo podemos agregar la función adicional correspondiente antes del método original, si ¿Desea agregar las funciones adicionales correspondientes antes y después del método original? Por ejemplo, la transacción debe abrir la transacción antes que el método original y cerrar la transacción después del método original para garantizar que nuestras operaciones sean atómicas. Bajo tales requisitos, la interfaz ¡No puede satisfacer nuestras necesidades!

Hemos considerado este inconveniente, obviamente los diseñadores de Spring también lo pensarán, por lo que Spring prepara otra interfaz para que nos ayude a resolver este problema.

MethodInterceptor (método interceptor):

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation var1) throws Throwable;
}

Su función es la misma que la interfaz MethodBeforeAdvice, pero también agrega funciones adicionales al método original, pero la diferencia es que MethodInterceptor puede agregar funciones adicionales antes y después del método original. Específicamente cómo se implementa, ¡continuaremos codificando!

#在这里重复的步骤我就不在贴出来了,就是简单的将上面的MethodBeforeAdvice实现添加额外功能中额外功能的代码替换成一下代码:


/**
 * @author : Carson-Zhao
 * @date : 2020/9/16 23:02
 */
public class OrderExtra2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("原始方法之前!");//原始方法前

        Object ret = methodInvocation.proceed();//原始方法回调

        System.out.println("原始方法之后!");//原始方法后

        return ret;
    }
}


#在xml中修改组装的额外功能

<bean id="orderExtra" class="com.xiaozhao.spring.service.extra.OrderExtra2"/>


#最后调用一下这两个方法

ClassPathXmlApplicationContext ctx =
                new ClassPathXmlApplicationContext("/applicationContext.xml");

        OrderService orderSevice = (OrderService) ctx.getBean("orderSevice");

        orderSevice.productOder();

        System.out.println("我是分割线================");

        orderSevice.consumOrder();


resultado de la operación:

análisis:

public class OrderExtra2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("原始方法之前!");//原始方法前

        Object ret = methodInvocation.proceed();//原始方法回调

        System.out.println("原始方法之后!");//原始方法后

        return ret;
    }
}

Hay una invocación de método en la interfaz MethodInterceptor, el parámetro de método es methodInvocation, el método de procedimiento que utiliza este parámetro puede volver a llamar al método original (callback significa ejecutar el método original) y devolver el valor de retorno del método original ( ¡nota! Si el método original Si no hay un valor de retorno, entonces el ret aquí es nulo ), podemos agregar nuestro código de función adicional (transacción, registro, monitoreo de rendimiento ... etc.) antes o después de esta línea de código y antes y después

Expansión:

Cuando se usa la interfaz MethodInterceptor para agregar funciones adicionales, si el método original arroja una excepción, se pueden agregar funciones adicionales en el bloque catch (escena: registro de excepciones). La implementación específica es un bloque simple try / catch, aquí está ¡No demuestre!

suplemento:

Aunque la interfaz MethodInterceptor es más fácil de usar y más común, no es perfecta. Uno de sus inconvenientes es que no es un inconveniente, es decir, después del proceso de devolución de llamada del método, se puede cambiar el valor de retorno del método original, cambiando así el valor de retorno del método original.

/**
 * @author : Carson-Zhao
 * @date : 2020/9/16 23:02
 */
public class OrderExtra2 implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        System.out.println("原始方法之前!");
        Object ret = methodInvocation.proceed();
        System.out.println("原始方法之后!");

        return true;//不将原始方法的返回值进行返回,就可以改变原始方法的返回值
        
    }
}

para resumir:

        Por el contrario, las dos interfaces MethodBeforeAdvice y MethodInterceptor se usan comúnmente en el desarrollo real. La primera se usa rara vez, por lo que aquí la primera es principalmente para comprender y la segunda para aprender a usarla. Pero el método de invocación en la interfaz MethodInterceptor puede cambiar el valor de retorno del método original.

Bueno, hasta ahora, solo hemos hablado sobre los principios básicos del proxy dinámico de una manera más general. Para comprender Spring Dynamic Proxy (AOP) más profundamente, sublimemos un poco y echemos un vistazo al principio de implementación subyacente.

Por el ejemplo anterior, sabemos que el proxy dinámico de Spring ya no necesita que escribamos clases de proxy manualmente, sino que solo debemos centrarnos en el procesamiento comercial de las clases comerciales. En comparación con el proxy estático, obviamente es una gran ventaja. Luego, piense si hay un proxy en este proceso. ¿clase? Si es así, ¿cómo crea AOP clases de proxy? ¿Cómo procesa Spring los objetos proxy?

Cómo crear la clase de proxy:

Proxy dinámico JDK:

Spring中动态代理类的相关细节分析:

动态代理类特点:

1.访问修饰符为public,final和非抽象类型的

2.继承了java.lang.reflect.Proxy类,实现了getProxyClass()和newProxyInstance方法

3.有一个public类型的构造方法,该方法有一个InvocationHandler类型的参数

4.当程序调用动态代理实例的方法时,会调用与之关联的InvocationHandler对象的invoke方法

扩展:

1、Proxy类的isProxyClass静态方法可以用来判断指定的类是否为动态代理类

De las características anteriores, podemos ver que la clase de proxy dinámico está estrechamente relacionada con java.lang.reflect.Proxy, así que echemos un vistazo al código fuente de Proxy para pretender ser analizado.


JDK实现的动态代理:

Object ProxyClass = Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

分析:

ProxyClass:动态代理类

classLoader:类加载器

interfaces: 原始类实现的接口

invocationHandler:额外功能



为了减省太多干扰信息,我就截取相关重要的部分源码进行强行分析

public class Proxy implements java.io.Serializable {

    //生成代理类实例
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //验证传入的InvocationHandler是否为空
        Objects.requireNonNull(h);
        
        //克隆代理类实现的接口
        final Class<?>[] intfs = interfaces.clone();
        //获得安全管理器
        final SecurityManager sm = System.getSecurityManager();
        //检查创建代理类所需的权限
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         * 查找或者生成特定的代理类(如果缓存中存在,则直接获取)
         */
        // 查找缓存或者生成一个特定的代理类对象(缓存中没有对应的代理对象时,就会去生成新的代理对象)—————————关键!!!!
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //权限校验
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //获取参数类型是InvocationHandler.class的代理类构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //传入InvocationHandler实例去构造一个代理类的实例,所有代理类都继承自Proxy,而Proxy构造方法需要InvocationHandler实例作为参数
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        //如果缓存中有实现给定接口的代理类存在,就返回缓存中的副本;否则,将通过ProxyClassFactory创建代理类
        return proxyClassCache.get(loader, interfaces);
    }

    //当缓存中不存在副本时,则由ProxyClassFactory生成
    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names 做一个标识所有代理类名称前缀
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names 用原子类来生成代理类的序号, 保证序号唯一
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //intf是否可以由指定的类加载进行加载
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.//判断intf是不是接口
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate//验证intf在数组中是不是重复的
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
            //生成代理类的包名
            String proxyPkg = null;     // package to define proxy class in
            //代理类的访问标志, 默认是public和final
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             * //验证所有非公共代理接口都在同一个包中
             */

            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                //如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
                if (!Modifier.isPublic(flags)) {
                    //生成代理类的访问标志设置改为final
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            //代理类如果实现不同包接口, 并且接口都不是public的, 那么就会在这里报错
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package 如果不存在非公正代理包则使用com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             * 生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg(包名) + proxyClassNamePrefix(类名前缀) + num(序号);

            /*
             * Generate the specified proxy class.
             * 生成代理代理的字节码!!!!——————————————————————动态代理的关键
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);//获取代理字节码文件
            try {
                //根据二进制文件生成相应的Class实例
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

    //根据指引我们来到generateProxyClass这个方法
    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {        
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);//构造ProxyGenerator对象
        final byte[] var4 = var3.generateClassFile();——————————————获取字节码!!!!!——————关键方法
        //如果saveGeneratedFiles为true,则表示需要保存生成的字节码文件,将字节码文件写入磁盘
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));//路径
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);//写入磁盘
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }
        //将字节码数组返回
        return var4;
    }

    //继续跟随以上方法,追溯ProxyGenerator.generateClassFile方法

    private byte[] generateClassFile() {
        //第一步、将所有方法组装成ProxyMethod对象,并为代理类生成toString, hashCode, equals等代理方法
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            //遍历每一个接口的每一个方法, 并生成ProxyMethod对象
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }
        //第二步、组装要生成的class文件的所有字段信息和方法信息
        Iterator var15;
        try {
            添加构造器
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();
            //遍历缓存中的代理方法
            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    //添加代理类的静态字段
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    //添加代理类的代理方法
                    this.methods.add(var16.generateMethod());
                }
            }
            //添加代理类的静态字段初始化方法
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            //第三步、写入最终class文件
              //验证常量池中存在代理类的全限定名
              this.cp.getClass(dotToSlash(this.className));
              //验证常量池中存在代理类父类的全限定名
              this.cp.getClass("java/lang/reflect/Proxy");
              var1 = this.interfaces;
              var2 = var1.length;
  
              //验证常量池存在代理类接口的全限定名
              for(var3 = 0; var3 < var2; ++var3) {
                  var4 = var1[var3];
                  this.cp.getClass(dotToSlash(var4.getName()));
              }
  
              //开始写入文件了,设置常量池只读
              this.cp.setReadOnly();
              ByteArrayOutputStream var13 = new ByteArrayOutputStream();
              DataOutputStream var14 = new DataOutputStream(var13);
  
              try {
                  //1.写入魔数
                  var14.writeInt(-889275714);
                  //2.写入次版本号
                  var14.writeShort(0);
                  //3.写入主版本号
                  var14.writeShort(49);
                  //4.写入常量池
                  this.cp.write(var14);
                  //5.写入访问修饰符
                  var14.writeShort(this.accessFlags);
                  //6.写入类索引
                  var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                  //7.写入父类索引, 生成的代理类都继承自Proxy
                  var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                  //8.写入接口计数值
                  var14.writeShort(this.interfaces.length);
                 
                 Class[] var17 = this.interfaces;
                int var18 = var17.length;
 
                 //9.写入接口集合
                 for(int var19 = 0; var19 < var18; ++var19) {
                     Class var22 = var17[var19];
                     var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                 }
                 //10.写入字段计数值
                 var14.writeShort(this.fields.size());
                 var15 = this.fields.iterator();
                 //11.写入字段集合 
                 while(var15.hasNext()) {
                     ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                     var20.write(var14);
                 }
                 //12.写入方法计数值
                 var14.writeShort(this.methods.size());
                 var15 = this.methods.iterator();
                 //13.写入方法集合
                 while(var15.hasNext()) {
                     ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                     var21.write(var14);
                 }
                  //14.写入属性计数值, 代理类class文件没有属性所以为0
                 var14.writeShort(0);
                 //转换成二进制数组输出
                 return var13.toByteArray();
             } catch (IOException var9) {
                 throw new InternalError("unexpected I/O Exception", var9);
             }
         }
    }
}

El código anterior puede ser más confuso y
    simple de resumir:

思想:通过类加载器获取类字节码,通过类实现的接口反射获得该类的属性和方法,并生成新的字节码文件

参数分析:

参数一:

类加载器————————创建实例对象所需的类加载器,可借用(前文有提及创建实例对象需要类加载器)

参数二:

原始类实现的接口———————JDK动态代理最重要的就是保证代理类和原始类实现相同的结果,代理类的代理方法中调用原始类的原始方法,所以我们这里需要传一个原始类实现的接口作为参数

参数三:

额外功能——————需要实现InvocationHandler接口的invoke方法

Agente dinámico CGLIB:

CGLIB动态代理原理和JDK动态代理原理有些类似

区别是:

 在Object ProxyClass =Proxy.newProxyInstance(classLoader,SuperClass,invocationHandler)
 这里的时候第二个参数不再是是原始类实现的接口,而是直接传原始类

    原因:代理类不再需要和原始类实现同一个接口去获取方法,而是直接继承原始类来获取原始方法


在这里Spring给我们提供了一个新的对象Enhancer,通过这个对象来set所需的三个参数(类加载器,原始类,额外功能)


例子:


/**
 * @author : Carson-Zhao
 * @date : 2020/9/11 23:44
 */
public class demo {

    public static void main(String[] args) {

        OrderServiceImpl orderService = new OrderServiceImpl();

        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(demo.class.getClassLoader());
        enhancer.setSuperclass(orderService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

                System.out.println("额外功能1");
                Object ret = method.invoke(orderService, args);
                System.out.println("额外功能2");
                return ret;
            }
        };
        enhancer.setCallback(interceptor);

        OrderService orderServiceProxy = (OrderService) enhancer.create();
        orderServiceProxy.productOder();
        orderServiceProxy.consumOrder();

    }
}

Los resultados del código anterior:

para resumir:

Hay dos formas para que AOP cree agentes dinámicos:

1. Proxy dinámico JDK

2. Agente dinámico CGLIB

La diferencia entre los dos:

JDK implementa el método original de llamar a la clase original implementando la misma interfaz entre la clase proxy y la clase original. CGLIB usa la clase proxy para implementar la clase original para llamar al método original en la clase original.

JDK ———— Implementar la misma interfaz implementada por la clase original

CGLIB ———— Heredar la clase original

 

persistente:

En este punto, el AOP se explica casi con claridad. Hay conceptos básicos y un poco de profundidad. Se considera consolidado después de ordenar los puntos de conocimiento. Espero que los amigos gordos que lean el artículo se animen juntos

plan:

El siguiente plan es aprender el empaque secundario de RedisTemplate, que se utiliza principalmente para nuestro desarrollo diario.

Al final:

Todos los que son grandes lo pasan mal, pero mientras persistas como SB, ¡serás genial!

Supongo que te gusta

Origin blog.csdn.net/weixin_43562937/article/details/108433077
Recomendado
Clasificación