Принцип Spring aop динамический прокси JDK и динамический прокси CGLIB

Spring

Двумя основными функциями Spring являются IOC и AOP. IOC отвечает за динамическое внедрение объектов в контейнер, чтобы добиться эффекта внедрения того, кому это нужно, и внедрения, когда это необходимо. Также важно понимать ioc Spring. Но сегодня я в основном пришел поговорить с вами об аоп. АОП широко используется для работы с некоторыми службами системного уровня со сквозными свойствами,Появление АОП — хорошее дополнение к ООП, используемый для решения сквозных задач, распределенных по различным модулям системы, таких как управление транзакциями, ведение журналов, кэширование и т. д.

Ключ к реализации АОП лежит в прокси-сервере АОП, автоматически создаваемом инфраструктурой АОП.

Прокси-серверы AOP в основном делятся на статические прокси-серверы и динамические прокси-серверы.

  • Представители статического агента AspectJ;
  • Динамический прокси представлен Spring AOP

1. АспектJ

AspectJ — это усовершенствование статических прокси-серверов, в котором используется генерация классов прокси-серверов АОП во время компиляции, поэтому его также называют улучшением времени компиляции, и оно имеет более высокую производительность. Недостаток: но его нужно обрабатывать с помощью специального компилятора.

2. Весенний АОП

Динамический прокси-сервер, используемый Spring AOP, генерирует класс прокси-сервера AOP во время выполнения.Так называемый динамический прокси-сервер означает, что структура AOP не изменяет байт-код, а временно создает объект AOP для метода в памяти.Этот объект AOP содержит цель Все методы, расширенная обработка в определенной точке и методы обратного вызова исходного объекта. Минусы: немного худшая производительность, поскольку Spring AOP должен генерировать прокси-серверы AOP при каждом запуске.

Поскольку использование аспекта также должно обрабатываться с помощью специального компилятора, это немного громоздко. Сегодня важно объяснить Spring AOP

Существует два основных способа динамического прокси-сервера Spring AOP: динамический прокси-сервер JDK и динамический прокси-сервер CGLIB.

  • Динамический прокси-сервер JDK получает проксируемый класс посредством отражения и требует, чтобы проксируемый класс реализовывал интерфейс. Ядром динамического прокси-сервера JDK является интерфейс InvocationHandler и класс Proxy.
  • Если целевой класс не реализует интерфейс, Spring AOP выберет использование CGLIB для динамического проксирования целевого класса. CGLIB (библиотека генерации кода) — это библиотека классов, сгенерированная кодом, которая может динамически генерировать подкласс класса во время выполнения (путем изменения байт-кода для реализации прокси). Обратите внимание, что CGLIB является динамическим прокси через наследование, поэтому, если класс помечен как final, он не может использовать CGLIB в качестве динамического прокси. Динамические агенты jdk и cglib работают вместе для реализации аспектно-ориентированных функций нашего aop.

Давайте используем простой код, чтобы продемонстрировать принцип реализации динамического прокси jdk и cglib.

Во-первых, динамический прокси-сервер jdk реализует перехват AOP.

  • 1. Определите интерфейс JdkInterface для целевого целевого класса, который является предпосылкой реализации динамического прокси jdk.
/**
 * Created by qcl on 2018/11/29
 * desc: jdk动态aop代理需要实现的接口
 */
public interface JdkInterface {
    public void add();
}
  • 2. Используйте целевой класс JdkClass, который мы хотим передать через прокси, для реализации интерфейса, который мы определили выше.Наша экспериментальная цель — реализовать перехват до и после метода add целевого класса без изменения целевого класса JdkClass и добавить пользовательскую логику аспекта. В этом прелесть АОП: между кодом и кодом нет связи.
/**
 * Created by qcl on 2018/11/29
 * desc: 被代理的类,即目标类target
 */
public class JdkClass implements JdkInterface {
    @Override
    public void add() {
        System.out.println("目标类的add方法");
    }
}

-3, к важному шагу, с нашим MyinvocationHandler, добиться интерфейса InvocationHandler и вызывать методы в реализации интерфейса. Более приближенный метод вызывающего вида является добавление логики раздела в этом методе. Целевой процесс выполнения класса выполняется Mehod.invoke (Target, Args) это утверждение.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by qcl on 2018/11/29
 * desc:这里加入切面逻辑
 */
public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before-------切面加入逻辑");
        Object invoke = method.invoke(target, args);//通过反射执行,目标类的方法
        System.out.println("after-------切面加入逻辑");
        return invoke;
    }
}
  • 4. Результаты испытаний
/**
 * Created by qcl on 2018/11/29
 * desc:测试
 */
public class JdkTest {
    public static void main(String[] args) {
        JdkClass jdkClass = new JdkClass();
        MyInvocationHandler handler = new MyInvocationHandler(jdkClass);
        // Proxy为InvocationHandler实现类动态创建一个符合某一接口的代理实例
        //这里的proxyInstance就是我们目标类的增强代理类
        JdkInterface proxyInstance = (JdkInterface) Proxy.newProxyInstance(jdkClass.getClass().getClassLoader(),
                jdkClass.getClass()
                        .getInterfaces(), handler);
        proxyInstance.add();
        //打印增强过的类类型
        System.out.println("=============" + proxyInstance.getClass());

    }
}

Выполните приведенный выше тестовый класс, чтобы получить следующие результаты.

结果
Видно, что логика пользовательского аспекта была добавлена ​​до и после метода добавления целевого класса, и вступает в силу механизм перехвата АОП. Посмотрите еще раз на класс com.sun.proxy.$Proxy0. Это еще раз доказывает, что ядром динамического прокси __JDK является интерфейс InvocationHandler и класс Proxy__.

Во-вторых, динамический прокси cglib реализует перехват АОП.

  • 1. Определите базовый целевой класс для проксирования (cglib не требует определения интерфейса)
/**
 * Created by qcl on 2018/11/29
 * desc:要被代理的类
 */
public class Base {
    public void add(){
        System.out.println("目标类的add方法");
    }
}
  • 2. Определите класс CGLibProxy для реализации интерфейса методоинтерфертификатора для реализации метода перехвата. Целью агента также является настраиваемая логика поверхности на заказ, а метод добавления класса целевого класса выполняет оператор как Proxy.invokeSuper (объект, args) до и после метода добавления.
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * Created by qcl on 2018/11/29
 * desc:这里加入切面逻辑
 */
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
            throws Throwable {
        System.out.println("before-------切面加入逻辑");
        methodProxy.invokeSuper(object, args);
        System.out.println("after-------切面加入逻辑");
        return null;
    }
}
  • 3. Тестовый класс
/**
 * Created by qcl on 2018/11/29
 * desc:测试类
 */
public class CglibTest {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Base.class);
        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
        enhancer.setCallback(proxy);
        //此刻,base不是单车的目标类,而是增强过的目标类
        Base base = (Base) enhancer.create();
        base.add();

        Class<? extends Base> baseClass = base.getClass();
        //查看增强过的类的父类是不是未增强的Base类
        System.out.println("增强过的类的父类:"+baseClass.getSuperclass().getName());
        System.out.println("============打印增强过的类的所有方法==============");
        FanSheUtils.printMethods(baseClass);


        //没有被增强过的base类
        Base base2 = new Base();
        System.out.println("未增强过的类的父类:"+base2.getClass().getSuperclass().getName());
        System.out.println("=============打印增未强过的目标类的方法===============");
        FanSheUtils.printMethods(base2.getClass());//打印没有增强过的类的所有方法

    }
}

Ниже результат печати

结果
Вы можете увидеть, распечатав результаты

  • cglib динамический перехват успешно отключен
  • Способ динамического прокси cglib заключается в динамическом создании подкласса целевого класса (базы) во время выполнения и добавлении множества методов, специфичных для cglib, на основе существующих методов целевого класса. Служебный класс, который печатает все методы класса с отражением, размещен ниже.
public class FanSheUtils {

    //打印该类的所有方法
    public static void printMethods(Class cl) {
        System.out.println();
        //获得包含该类所有其他方法的数组
        Method[] methods = cl.getDeclaredMethods();
        //遍历数组
        for (Method method : methods) {
            System.out.print("  ");
            //获得该方法的修饰符并打印
            String modifiers = Modifier.toString(method.getModifiers());
            if (modifiers.length() > 0) {
                System.out.print(modifiers + " ");
            }
            //打印方法名
            System.out.print(method.getName() + "(");

            //获得该方法包含所有参数类型的Class对象的数组
            Class[] paramTypes = method.getParameterTypes();
            //遍历数组
            for (int i = 0; i < paramTypes.length; i++) {
                if (i > 0) {
                    System.out.print(",");
                }
                System.out.print(paramTypes[i].getName());
            }
            System.out.println(");");
        }
    }
}

Примечание: cglib.jar и asm.jar используются выше. Внесите следующую библиотеку классов в pom.xml нашего maven.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

от Rice Cake Mom qcl