Принцип реализации динамического прокси Java

Java Язык программирования

Что касается динамического прокси в Java, первое, что нам нужно понять, это общий шаблон проектирования — прокси-паттерн, а прокси, в зависимости от момента времени создания класса прокси, можно разделить на статические прокси и динамические. прокси.

1. Агентский режим

Шаблон прокси является распространенным шаблоном проектирования Java. Его характеристикой является то, что класс прокси имеет тот же интерфейс, что и класс делегата. Класс прокси в основном отвечает за предварительную обработку сообщений для класса делегата, фильтрацию сообщений, пересылку сообщений в классы делегата, и постобработка сообщений. Между классом-посредником и классом-делегатом обычно существует ассоциативное отношение. Объект класса-посредника связан с объектом класса-делегата. методы объекта класса делегата, предоставляют определенные услуги.

Проще говоря, когда мы обращаемся к фактическому объекту, мы получаем доступ к нему через прокси-объект. Режим прокси вводит определенную степень косвенности при доступе к фактическому объекту. Из-за этой косвенности может быть подключено несколько вариантов использования. Я объясню преимущества этой косвенности позже. Схема структуры прокси-режима (картинка из "Dahua Design Mode"):

2. Статический прокси

1. Статический прокси

Статический прокси: исходный код создается программистом или автоматически генерируется специальным инструментом, то есть интерфейс, прокси-класс, прокси-класс и т. д. определяются во время компиляции. Перед запуском программы создается файл .class прокси-класса.

2. Простая реализация статического прокси

В соответствии с приведенной выше диаграммой классов режима прокси, давайте напишем простой пример статического прокси. Приведу грубый пример: если одноклассник хочет заплатить учителю плату за урок, но все они передают свои деньги учителю через монитор. Здесь наблюдатель является агентом студентов по оплате платы за занятие, а наблюдатель является агентом студентов.

Во-первых, мы создаем интерфейс Person. Этот интерфейс является общедоступным интерфейсом ученика (прокси-класса) и монитора (прокси-класса), и все они ведут себя как платные классы. Таким образом, учащиеся могут заплатить за занятие и позволить наблюдателю выполнять его в качестве агента.

Класс Student реализует интерфейс Person. Учащиеся могут специально реализовать действие по оплате платы за занятие.

Студенческий прокси, этот класс также реализует интерфейс Person, но также содержит объект класса студента.Поскольку он реализует интерфейс Peson и одновременно содержит объект студента, он может выступать в качестве прокси для объекта класса студента для оплаты за обучение. поведение класса (выполнить метод giveMoney()).

Давайте проверим, как использовать режим прокси:

результат операции:

Плата за смену осуществляется не Чжан Санем (прокси-объектом), а командиром отряда (прокси-объектом). Это режим прокси.

Главное в режиме прокси — иметь публичный интерфейс (Person), конкретный класс (Student) и класс прокси (StudentsProxy). Как упоминалось выше, шаблон прокси должен ввести определенную степень косвенности при доступе к фактическому объекту, и из-за этой косвенности может быть присоединено несколько применений. Косвенность здесь означает, что метод фактического объекта не вызывается напрямую, поэтому мы можем добавить некоторые другие варианты использования в прокси-процессе.

Для этого примера, прежде чем присоединиться к командиру отряда, он хотел отразить, что Чжан Сан добился больших успехов в своих недавних исследованиях, прежде чем помочь Чжан Саню оплатить смену.Это можно легко сделать с помощью модели агентства:

результат операции:

Видно, что необходимо только выполнить другие операции, прежде чем помочь Чжан Сану оплатить смену в классе прокси. Эта операция также является большим преимуществом использования прокси-режима. Наиболее простым является Аспектно-ориентированное программирование (АОП) в Spring.Мы можем выполнять некоторые операции до pointcut и выполнять некоторые операции после pointcut.Этот pointcut является методом. Класс, в котором находятся эти методы, должен быть проксирован, а некоторые другие операции вырезаны в прокси-процессе.

3. Динамический агент

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

Метод прокси, созданный классом прокси во время работы программы, называется динамическим прокси. В приведенном выше примере статического прокси класс прокси (studentProxy) определяется сам по себе и компилируется перед запуском программы. Однако динамический прокси, класс прокси не определен в коде Java, а динамически генерируется во время выполнения в соответствии с нашими «инструкциями» в коде Java.

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

Здесь есть только один метод giveMoney, просто напишите метод beforeMethod один раз, но если в giveMonney есть много других методов, вам нужно написать метод beforeMethod много раз, что хлопотно. Тогда посмотрите, как реализован динамический прокси ниже.

2. Простая реализация динамического прокси

Класс Proxy и интерфейс InvocationHandler предоставляются в пакете java.lang.reflect java. С помощью этого класса и этого интерфейса можно создавать динамические прокси-классы JDK и динамические прокси-объекты.

Шаги по созданию динамического прокси-объекта см. в конкретном коде ниже:

  • Создайте объект InvocationHandler

//Создать InvocationHandler, связанный с прокси objectInvocationHandlerstuHandler=newMyInvocationHandler(stu);

  • Используйте статический метод getProxyClass класса Proxy для создания динамического прокси-класса stuProxyClass.

Class<?>stuProxyClass=Proxy.getProxyClass(Person.class.getClassLoader(),newClass<?>[]{Person.class});

  • Получить конструктор с параметром InvocationHandler в stuProxyClass

Constructor<?>constructor=PersonProxy.getConstructor(InvocationHandler.class);

  • Создайте динамический экземпляр stuProxy через конструктор

PersonstuProxy=(Person)cons.newInstance(stuHandler);

На этом этапе создается динамический прокси-объект.Конечно, вышеописанные четыре шага можно упростить с помощью метода newProxyInstances класса Proxy:

В этот момент вы точно будете сильно растеряны.Как выполняется этот динамический прокси?Как объект прокси выполняет методы проксируемого объекта?Давайте не будем торопиться, давайте посмотрим на простой и полный пример динамического прокси. В приведенном выше примере со статическим прокси монитор должен платить за занятия для студентов.

Во-первых, определить интерфейс Person:

Создайте фактический класс, который необходимо проксировать:

Затем определите класс инструмента для определения времени выполнения метода. Перед выполнением любого метода сначала вызовите метод start, а после выполнения вызовите метод finsh, чтобы рассчитать время выполнения метода. Это также самое простое время выполнения метода. средство обнаружения.

Создайте класс StuInvocationHandler, реализуйте интерфейс InvocationHandler и сохраните целевой экземпляр прокси-объекта в этом классе. В InvocationHandler есть метод вызова, и все методы, выполняющие прокси-объект, будут заменены выполнением метода вызова.

Затем выполните соответствующий метод целевого объекта прокси-объекта в методе вызова. Конечно, в прокси-процессе мы добавляем нашу собственную другую обработку перед фактическим выполнением методов проксируемого объекта. Это также основной принцип реализации АОП в Spring, который также требует очень важных базовых знаний об отражении в Java.

После завершения вышеуказанной работы мы можем специально создавать динамические прокси-объекты.Выше кратко описано, как создавать динамические прокси-объекты.Мы создаем динамические прокси-объекты упрощенным способом:

Выполняем этот класс ProxyTest, сначала подумаем, мы создали студента Чжан Сана, которого нужно проксировать, и передали объект чжансана в stuHandler. Все методы выполнения прокси-объекта будут заменены выполнением метода вызова, то есть метод вызова в StuInvocationHandler выполняется последним. Поэтому естественно видеть следующие результаты.

результат операции:

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

В процессе динамического прокси отношения между прокси-объектом и прокси-объектом не такие четкие и понятные, как статические прокси. Поскольку в процессе динамического прокси мы фактически не видели прокси-класса, а также мы не видели четко конкретного вида прокси-класса, а прокси-объект и прокси-объект в динамическом прокси-процессе, завершенном InvocationHandler, что является конкретной операцией и почему методы, выполняемые прокси-объектом, выполняются через метод вызова в InvocationHandler. С этими проблемами нам нужно кратко проанализировать исходный код динамического агента java, чтобы выяснить причину.

4. Анализ принципа динамического действия

1. Класс динамического прокси, созданный динамическим прокси Java.

Выше мы использовали метод newProxyInstance класса Proxy для создания динамического прокси-объекта.Посмотрев исходный код метода, мы обнаружили, что он только инкапсулирует шаги создания динамического прокси-класса (красная стандартная часть):

На самом деле, больше всего нам следует обратить внимание на Class>cl=getProxyClass0(loader,intfs); Это предложение, класс прокси генерируется здесь, а конструктор в коде позади также получается через сгенерированный здесь класс. Видно, что это Генерация класса является ключом ко всему динамическому прокси.Поскольку это динамически сгенерированный файл класса, я не буду вдаваться в анализ того, как здесь генерируется файл класса.Мне нужно только знать что файл класса кэшируется на виртуальной машине Java.Мы можем передать следующее Метод печатает его в файл и видит реальное содержимое:

Декомпилируем этот файл класса, давайте посмотрим, какой контент генерирует для нас jdk:

JDK создает для нас прокси-класс с именем $Proxy0 (0 за этим именем — это число, и существует несколько прокси-классов, которые будут увеличиваться одновременно). Этот файл класса помещается в память. Когда мы создаем прокси-объект, Конструктор этого класса получается через отражение, а затем создается экземпляр прокси. Просмотрев исходный код сгенерированного класса прокси, мы можем легко увидеть конкретный процесс реализации динамического прокси.

Мы можем рассматривать InvocationHandler как промежуточный класс, промежуточный класс содержит проксируемый объект, и соответствующий метод проксируемого объекта вызывается в методе вызова. Удерживая ссылку на проксируемый объект посредством агрегирования, внешние вызовы для вызова окончательно преобразуются в вызовы проксируемого объекта.

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

V. Резюме

Сгенерированный класс прокси: $Proxy0extendsProxyimplementsPerson, мы видим, что класс прокси наследует класс Proxy, поэтому определено, что динамический прокси java может только проксировать интерфейс.Механизм наследования Java предназначен для того, чтобы эти динамические классы прокси не могли реализовать динамический класса актерского мастерства.

Вышеуказанный пример динамического прокси на самом деле представляет собой простую реализацию AOP, который обрабатывается до и после выполнения метода целевого объекта, а также с трудоемкой статистики метода. Реализация AOP Spring на самом деле использует прокси-сервер и invocationHandler.

(автор)

Автор л Гонджян

Источник: https://www.cnblogs.com/gonjan-blog/p/6685611.html.

(рекомендуется к прочтению)

Основной принцип реализации MyBatis! Это использование динамического прокси~

(Заканчивать)

(java ментальная карта)

Нажмите и удерживайте, чтобы следовать, java каждый день, станьте архитектором