Динамический прокси Java, достаточно прочитать это

Java задняя часть GitHub Spring

В этой статье необходимо использовать знания об отражении в Java.Если вы не понимаете, что такое отражение, вы можете сначала перейти сюда.«Java Advanced — отражение».

Идеи программирования приходят из жизни, а «агенты» очень распространены в жизни. Например, когда мы что-то покупаем, мы обычно не покупаем это напрямую с фабрики, а идем в магазин или к другим торговцам, чтобы купить это.То, что эти торговцы делают, является агентом. Другой пример: друзья, которые занимаются микробизнесом, часто продвигают товары в кругу друзей, а также являются агентами. Агент в Java-программе реализован с интерфейсом, интерфейсом можно считать спрос пользователя на покупку определенного товара, а агентом, который реально продает товар пользователю, является агент. связанные с этим спросом. , агент имеет отношения с производителем (агент и агент), что на картинке ниже.

代理模式.png

Здесь пользователя на рисунке можно рассматривать как человека, покупающего вещи, прокси-интерфейс можно рассматривать как некий товар, прокси можно рассматривать как промежуточного агента, а реальный прокси-объект можно рассматривать как фабрику, которая производит товары или производителя. Давайте возьмем это в качестве примера для реализации режима прокси.

Примечание. Здесь все классы помещены в папку, а внутренние классы для удобства оформлены статичными.

public class StaticProxy {
    // 商品接口
    static interface Goods {
        public void trade();
    }

    // 商品产地类
    static class ChangJia implements Goods {
        @Override
        public void trade() {
            System.out.println("厂家生产商品");
        }
    }

    // 商品代理类
    static class JingXiaoShang implements Goods {
        private ChangJia changJia;

        public JingXiaoShang(ChangJia changJia) {
            this.changJia = changJia;
        }

        @Override
        public void trade() {
            System.out.println("厂家生产产品,成本为1000元");
            changJia.trade();
            System.out.println("经销商卖出商品,利润为100元");
        }
    }

    // 用户购买东西
    public static void main(String[] args) {
        Goods proxy = new JingXiaoShang(new ChangJia());
        proxy.trade(); // 商品被交易
    }
}

Выход

厂家生产产品,成本为1000元
厂家生产商品
经销商卖出商品,利润为100元

Вышеприведенный код является реализацией прокси-режима, т.е.«Статический прокси», то есть прокси-класс был определен во время компиляции программы, а также может быть реализован на Java«Динамический прокси», прокси-класс создается при запуске программы.

Существует два способа реализации динамического прокси-сервера в Java: один — это динамический прокси-сервер JDK, поставляемый с Java, а другой — динамический прокси-сервер библиотеки CGLIB, реализованный с использованием технологии улучшения байт-кода.

1. Динамический прокси JDK

Динамические прокси-серверы JDK по существу используют отражение в Java. Метод определен в интерфейсе (здесь интерфейс Goods), а прокси-класс (здесь класс ChangJia) должен реализовать интерфейс, а затем реализовать методы в интерфейсе. При вызове метода в интерфейсе перехватывать выполняемый метод, добавлять другие операции, а так же нужен класс обработки (здесь класс GoodsHander) который занимается перехватом.Этот класс обработки реализует интерфейс InvocationHandler, где вызывается invoke() метод будет вызываться в. Методы в интерфейсе выполняются первыми, которые действуют как прокси. В отличие от реализации в приведенном выше режиме прокси, динамический прокси JDK не определяет классы прокси напрямую, а добавляет в интерфейс класс обработки для методов. выполнить метод. Для получения подробной информации, пожалуйста, внимательно прочитайте код и комментарии ниже.

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

public class JDKDynamicProxy {
    // 商品接口
    static interface Goods {
        public void trade();
    }

    // 商品产地类
    static class ChangJia implements Goods {
        @Override
        public void trade() {
            System.out.println("厂家生产商品");
        }
    }

    // 商品处理类
    static class GoodsHander implements InvocationHandler {
        private Object object; // 要代理的对象,这里为商品

        public GoodsHander(Object object) {
            this.object = object;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // proxy 为生成的代理对象
            System.out.println("厂家生产产品,成本为1000元");
            Object result = method.invoke(object, args);
            System.out.println("经销商卖出商品,利润为100元");
            return result;
        }
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        GoodsHander goodsHander = new GoodsHander(new ChangJia()); // GoodHander类和ChangJia打交道
        // 先动态生成代理类,再生成代理类的对象
//        Class proxyClass = Proxy.getProxyClass(Goods.class.getClassLoader(),Goods.class.getInterfaces());
//        Goods proxy = (Goods) proxyClass.getConstructor(GoodsHander.class).newInstance(goodsHander);
        // 动态生成的代理对象,一步到位
        Goods proxy = (Goods) Proxy.newProxyInstance(Goods.class.getClassLoader(), Goods.class.getInterfaces(), goodsHander);
        proxy.trade(); // 执行的是GoodsHander中的 invoke() 方法,然后是ChangJia中的 trade() 方法
    }
}

2. Динамический прокси CGLIB

Приведенный выше динамический прокси JDK должен определить интерфейс, а класс реализации реализует методы в интерфейсе.Если класс реализации не может реализовать интерфейс, мы не можем использовать вышеуказанный метод для прокси, но можем использовать следующий динамический прокси CGLIB. Технология улучшения байт-кода, используемая динамическим прокси CGLIB, то есть для генерации подкласса класса из файла скомпилированного класса (байт-кода). При полиморфизме вызов метода в родительском классе фактически вызывает соответствующий метод в дочернем классе. Следовательно, из-за наследования проксируемый класс или метод не должен модифицироваться ключевым словом final. Этот подкласс создается для запуска в программе, поэтому использование библиотеки CGLIB также является динамическим прокси. При использовании CGLIB необходимо импортировать jar-пакет cglib и зависимый jar-пакет asm.jar, который является ядром технологии расширения байт-кода. Использование библиотеки CGLIB не требует интерфейса.Добавлен класс расширения метода.Подкласс прокси-класса создается с помощью класса расширения Enhance в библиотеке CGLIB.Для получения дополнительной информации вы можете внимательно прочитать код и комментарии ниже.

Примечание: Перед использованием динамического прокси CGLIB необходимо импортировать соответствующие пакеты jar.Вы можете импортировать пакет cglib-*.jar и пакет asm-.jar по отдельности (обратите внимание на комбинацию двух версий) или только импортировать один пакет cglib-nodep-*.jar (включая asm). ссылка для скачивания:GitHub.com/Через забор/Через забор…

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBDynamicProxy {
    // 商品产地类
    static class ChangJia{
        public void trade() {
            System.out.println("厂家生产商品");
        }
    }

    // 方法增强类
    static class GoodsTrade implements MethodInterceptor {
        @Override
        // o 代表要增强的对象,method代表要拦截的方法,objects 代表方法中的参数,methodProxy 代表对方法的代理
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("厂家生产产品,成本为1000元");
            methodProxy.invokeSuper(object, objects);
            System.out.println("经销商卖出商品,利润为100元");
            return object;
        }

        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer(); // 增强类对象
            enhancer.setSuperclass(ChangJia.class); // 设置要增强的类,这里为 ChangJia
            GoodsTrade goodsTrade = new GoodsTrade();
            enhancer.setCallback(goodsTrade); // 设置要增强的方法,这里为 GoodsTrade
            ChangJia changJia = (ChangJia) enhancer.create(); // 生成增强过的子类对象
            changJia.trade(); // 调用方法实际为增强过的方法
        }
    }
}

Разница между двумя методами динамического прокси:

  • Динамический прокси-сервер JDK использует технологию отражения, и класс, который будет проксироваться, должен реализовывать интерфейс метода.
  • Динамический прокси-сервер CGLIB использует технологию улучшения байт-кода, и классу прокси не нужно реализовывать интерфейс метода.

В знаменитой среде Spring используются эти два прокси-метода Spring будет динамически переключаться между динамическим прокси-сервером JDK и CGLIB в зависимости от того, реализует ли интерфейс прокси-класс. Кроме того, АОП для аспектно-ориентированного программирования также является воплощением идеи динамического прокси. Путем вплетения новых методов до и после выполнения метода можно добиться эффекта усиления метода. Динамический прокси широко используется в рамки.

编程心路