42 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Динамический прокси Java: что это и как им пользоваться?

Динамический прокси / Java Dynamic Proxy

Не смотря на мой не большой опыт в java я все же могу по праву считать себя фанатиком, хлебом не корми дай поковыряться в чем нибудь и вот после очередного отвлечения на Wicket приходится вновь освежать память с чем мне обычно помогают такие люди как Юрий Ткач и Евгений Матюшкин(Skipy) своими статьями и видео за что им собственно спасибо. Так как сижу уже примерно пол года в пассивном поиске работы, фриланс спокойно позволяет заниматься любимым делом и не рваться в лапы работорговли, занимаюсь по мелочи своими стартапами. Учу что-то новое, вот в очередной раз перечитывая статью Skipy про «синхронизацию GUI« вновь встретился с такой полезной штукой как динамический прокси и поймав себя на мысли что совсем не помню что и зачем решил сделать заметку на будущее.

История

Концепция динамических прокси-классов появилась еще в 2000 году в JDK 1.3.
С тех ее используют все, кому не лень. Более того, она стала даже де-факто стандартом в некоторых областях java.

Пример

Реализуем интерфейс IUser

InvocationHandler — интерфейс, реализованный обработчиком вызова экземпляра прокси. У каждого экземпляра прокси есть связанный обработчик вызова. Когда метод вызывается на экземпляр прокси, вызов метода кодируется и диспетчеризируется invoke метод его обработчика вызова.

Далее как пользоваться… чудеса))

Ограничения и свойства

Немного теории… Создается прокси-класс с помощью вызова метода Proxy.getProxyClass, который принимает класс-лоадер и массив интерфейсов (interfaces), а возвращает объект класса java.lang.Class, который загружен с помощью переданного класс-лоадера и реализует переданный массив интерфейсов.

На передаваемые параметры есть ряд ограничений:
  1. Все объекты в массиве interfaces должны быть интерфейсами. Они не могут быть классами или примитивами.
  2. В массиве interfaces не может быть двух одинаковых объектов.
  3. Все интерфейсы в массиве interfaces должны быть загружены тем класс-лоадером, который передается в метод getProxyClass.
  4. Все не публичные интерфейсы должны быть определены в одном и том же пакете, иначе генерируемый прокси-класс не сможет их все реализовать.
  5. Ни в каких двух интерфейсах не может быть метода с одинаковым названием и сигнатурой параметров, но с разными типами возвращаемого значения.
  6. Длина массива interfaces ограничена 65535-ю интерфейсами. Никакой Java-класс не может реализовывать более 65535 интерфейсов (а так хотелось!).

Если какое-либо из вышеперечисленных ограничений нарушено — будет выброшено исключение IllegalArgumentException, а если массив интерфейсов interfaces равен null, то будет выброшено NullPointerException.

Свойства динамического прокси-класса
  1. Прокси-класс является публичным, снабжен модификатором final и не является абстрактным.
  2. Имя прокси-класса по-умолчанию не определено, однако начинается на $Proxy. Все пространство имен, начинающихся на $Proxy зарезервировано для прокси-классов.
  3. Прокси-класс наследуется от java.lang.reflect.Proxy.
  4. Прокси-класс реализует все интерфейсы, переданные при создании, в порядке передачи.
  5. Если прокси-класс реализует непубличный интерфейс, то он будет сгенерирован в том пакете, в котором определен этот самый непубличный интерфейс. В общем случае пакет, в котором будет сгенерирован прокси-класс неопределен.
  6. Метод Proxy.isProxyClass возвращает true для классов, созданных с помощью Proxy.getProxyClass и для классов объектов, созданных с помощью Proxy.newProxyInstance и false в противном случае. Данный метод используется подсистемой безопасности Java и нужно понимать, что для класса, просто унаследованного от java.lang.reflect.Proxy он вернет false.
  7. java.security.ProtectionDomain для прокси-класса такой же, как и для системных классов, загруженных bootstrap-загрузчиком, например — для java.lang.Object. Это логично, потому что код прокси-класса создается самой JVM и у нее нет причин себе не доверять.
Экземпляр динамического прокси-класса и его свойства

Конструктор прокси-класса принимает один аргумент — реализацию интерфейса InvocationHandler. Соответственно, объект прокси-класса можно создать с помощью рефлексии, вызвав метод newInstance объекта класса Class. Однако, существует и другой способ — вызвать метод Proxy.newProxyInstance, который принимает на вход загрузчик классов, массив интерфейсов, которые будет реализовывать прокси-класс, и объект, реализующий InvocationHandler. Фактически, данный метод комбинирует получение прокси-класса с помощью Proxy.getProxyClass и создание экземпляра данного класса через рефлексию.

Свойства созданного экземпляра прокси-класса следующие:

  1. Объект прокси-класса приводим ко всем интерфейсам, переданным в массиве interfaces. Если IDemo — один из переданных интерфейсов, то операция proxy instanceof IDemo всегда вернет true, а операция (IDemo) proxy завершится корректно.
  2. Статический метод Proxy.getInvocationHandler возвращает обработчик вызовов, переданный при создании экземпляра прокси-класса. Если переданный в данный метод объект не является экземпляром прокси-класса, то будет выброшено IllegalArgumentException исключение.
  3. Класс-обработчик вызовов реализует интерфейс InvocationHandler, в котором определен метод invoke, имеющий следующую сигнатуру:

Здесь proxy — экземпляр прокси-класса, который может использоваться при обработке вызова того или иного метода. Второй параметр — method является экземпляром класса java.lang.reflect.Method. Значение данного параметра — один из методов, определенных в каком-либо из переданных при создании прокси-класса интерфейсов или их супер-интерфейсов. Третий параметр — массив значений аргументов метода. Аргументы примитивных типов будут заменены экземплярами своих классов-оберток, таких как java.lang.Boolean или java.lang.Integer. Конкретная реализация метода invoke может изменять данный массив.

Значение, возвращаемое методом invoke должно иметь тип, совместимый с типом значения, возвращаемого интерфейсным методом, для которого вызывается данная обертка. В частности, если интерфейсный метод возвращает значение примитивного типа — необходимо возвратить экземпляр класса-обертки данного примитивного типа. Если возвращается null, а ожидается значение примитивного типа, — будет выброшено NullPointerException. В случае непримитивных типов, класс возвращаемого значения метода invoke должен быть приводим к классу возвращаемого значения интерфейсного метода, иначе будет выброшено ClassCastException.

Внутри метода invoke должны бросаться только те проверяемые исключения, которые определены в сигнатуре вызываемого интерфейсного метода либо приводимые к ним. Помимо этих типов исключений разрешается бросать только непроверяемые исключения (такие как java.lang.RuntimeException) или ошибки (например, java.lang.Error). Если внутри метода invoke выброшено проверяемое исключение несопоставимое с описанными в сигнатуре интерфейсного метода — то будет так же выброшено исключение UndeclaredThrowableException.

Методы hashCode, equals и toString, определенные в классе Object, так же будут вызываться не на прямую, а через метод invoke наравне со всеми интерфейсными методами. Другие публичные методы класса Object будут вызываться напрямую.

Магия за кулисами

Объект User — вполне обычный, никакой магии.

Proxy.newProxyInstance — сами истоки магии, параметры вызова следующие:

  • ClassLoader класса User, о нем немного ниже;
  • Массив типаClass, должен принимать массив интерфейсов, которые реализует наш класс (User). МЕТОДЫ ЭТИХ ИНТЕРФЕЙСОВ БУДУТ ПЕРЕХВАТЫВАТЬСЯ (invocationHandler-ом).
  • Экземпляр InvocationHandler, который будет перехватыватьметоды вызываемые для объекта user (на самом деле, вызовы будут идти через вновь созданный userProxy).

На выходе получаем экземпляр некого класса(прокси), дающий следующую магическую функциональность :

Читать еще:  SharewareOnSale — программы бесплатно и с большими скидками

Блог сурового челябинского программиста

Are you aware how much time I’ve spent learning for details of Java? Thread management, dynamics, CORBA.

воскресенье, 11 апреля 2010 г.

Об использовании динамических Proxy-классов в Java

Динамические прокси-классы

Сегодня мы поговорим о такой интересной особенности JVM, как динамические прокси-классы. Предположим, что у нас есть класс A, реализующий некоторые интерфейсы. Java-машина во время исполнения может сгенерировать прокси-класс для данного класса A, т.е. такой класс, который реализует все интерфейсы класса A, но заменяет вызов всех методов этих интерфейсов на вызов метода InvocationHandler#invoke, где InvocationHandler – интерфейс JVM, для которого можно определять свои реализации.

Создается прокси-класс с помощью вызова метода Proxy.getProxyClass, который принимает класс-лоадер и массив интерфейсов (interfaces), а возвращает объект класса java.lang.Class, который загружен с помощью переданного класс-лоадера и реализует переданный массив интерфейсов.

На передаваемые параметры есть ряд ограничений:

1. Все объекты в массиве interfaces должны быть интерфейсами. Они не могут быть классами или примитивами.

2. В массиве interfaces не может быть двух одинаковых объектов.

3. Все интерфейсы в массиве interfaces должны быть загружены тем класс-лоадером, который передается в метод getProxyClass.

4. Все не публичные интерфейсы должны быть определены в одном и том же пакете, иначе генерируемый прокси-класс не сможет их все реализовать.

5. Ни в каких двух интерфейсах не может быть метода с одинаковым названием и сигнатурой параметров, но с разными типами возвращаемого значения.

6. Длина массива interfaces ограничена 65535-ю интерфейсами. Никакой Java-класс не может реализовывать более 65535 интерфейсов (а так хотелось!).

Если какое-либо из вышеперечисленных ограничений нарушено – будет выброшено исключение IllegalArgumentException, а если массив интерфейсов interfaces равен null, то будет выброшено NullPointerException.

Свойства динамического прокси-класса

Необходимо сказать пару слов о свойствах класса, создаваемого с помощью Proxy.getProxyClass. Данные свойства следующие:

1. Прокси-класс является публичным, снабжен модификатором final и не является абстрактным.

2. Имя прокси-класса по-умолчанию не определено, однако начинается на $Proxy. Все пространство имен, начинающихся на $Proxy зарезервировано для прокси-классов.

3. Прокси-класс наследуется от java.lang.reflect.Proxy.

4. Прокси-класс реализует все интерфейсы, переданные при создании, в порядке передачи.

5. Если прокси-класс реализует непубличный интерфейс, то он будет сгенерирован в том пакете, в котором определен этот самый непубличный интерфейс. В общем случае пакет, в котором будет сгенерирован прокси-класс неопределен.

6. Метод Proxy.isProxyClass возвращает true для классов, созданных с помощью Proxy.getProxyClass и для классов объектов, созданных с помощью Proxy.newProxyInstance и false в противном случае. Данный метод используется подсистемой безопасности Java и нужно понимать, что для класса, просто унаследованного от java.lang.reflect.Proxy он вернет false.

7. java.security.ProtectionDomain для прокси-класса такой же, как и для системных классов, загруженных bootstrap-загрузчиком, например – для java.lang.Object. Это логично, потому что код прокси-класса создается самой JVM и у нее нет причин себе не доверять.

Экземпляр динамического прокси-класса и его свойства

Конструктор прокси-класса принимает один аргумент – реализацию интерфейса InvocationHandler. Соответственно, объект прокси-класса можно создать с помощью рефлексии, вызвав метод newInstance объекта класса Class. Однако, существует и другой способ – вызвать метод Proxy.newProxyInstance, который принимает на вход загрузчик классов, массив интерфейсов, которые будет реализовывать прокси-класс, и объект, реализующий InvocationHandler. Фактически, данный метод комбинирует получение прокси-класса с помощью Proxy.getProxyClass и создание экземпляра данного класса через рефлексию.

Свойства созданного экземпляра прокси-класса следующие:

1. Объект прокси-класса приводим ко всем интерфейсам, переданным в массиве interfaces. Если IDemo – один из переданных интерфейсов, то операция proxy instanceof IDemo всегда вернет true, а операция (IDemo) proxy завершится корректно.

2. Статический метод Proxy.getInvocationHandler возвращает обработчик вызовов, переданный при создании экземпляра прокси-класса. Если переданный в данный метод объект не является экземпляром прокси-класса, то будет выброшено IllegalArgumentException исключение.

3. Класс-обработчик вызовов реализует интерфейс InvocationHandler, в котором определен метод invoke, имеющий следующую сигнатуру:

Здесь proxy – экземпляр прокси-класса, который может использоваться при обработке вызова того или иного метода. Второй параметр – method является экземпляром класса java.lang.reflect.Method. Значение данного параметра – один из методов, определенных в каком-либо из переданных при создании прокси-класса интерфейсов или их супер-интерфейсов. Третий параметр – массив значений аргументов метода. Аргументы примитивных типов будут заменены экземплярами своих классов-оберток, таких как java.lang.Boolean или java.lang.Integer. Конкретная реализация метода invoke может изменять данный массив.

Значение, возвращаемое методом invoke должно иметь тип, совместимый с типом значения, возвращаемого интерфейсным методом, для которого вызывается данная обертка. В частности, если интерфейсный метод возвращает значение примитивного типа – необходимо возвратить экземпляр класса-обертки данного примитивного типа. Если возвращается null, а ожидается значение примитивного типа, – будет выброшено NullPointerException. В случае непримитивных типов, класс возвращаемого значения метода invoke должен быть приводим к классу возвращаемого значения интерфейсного метода, иначе будет выброшено ClassCastException.

Внутри метода invoke должны бросаться только те проверяемые исключения, которые определены в сигнатуре вызываемого интерфейсного метода либо приводимые к ним. Помимо этих типов исключений разрешается бросать только непроверяемые исключения (такие как java.lang.RuntimeException) или ошибки (например, java.lang.Error). Если внутри метода invoke выброшено проверяемое исключение несопоставимое с описанными в сигнатуре интерфейсного метода – то будет так же выброшено исключение UndeclaredThrowableException.

Методы hashCode, equals и toString, определенные в классе Object, так же будут вызываться не на прямую, а через метод invoke наравне со всеми интерфейсными методами. Другие публичные методы класса Object будут вызываться напрямую.

Пример: использование прокси-классов для обобщенного DAO

Давайте рассмотрим обещанный пример использования динамических прокси-классов. Идея взята из статьи Не повторяйте DAO!, только мы попробуем реализовать ее без использования Spring. Суть в следующем: у нас есть Hibernate, в котором есть такое понятие, как именованные запросы. Мы имеем много DAO для разных типов сущностей, в которых есть методы поиска объектов по каким-либо критериям, подсчет количества объектов и т.д. Причем, каждый метод, фактически, просто вызывает тот или иной именованный запрос и возвращает его результат. Непонятно, зачем плодить методы с одной и той же логикой. Можно просто определять методы в соответствующих интерфейсах, а вызывать их через прокси к данным интерфейсам. В прокси же вызов метода подменяется на вызов соответствующего именованного запроса (имя которого может вычисляться, например, по формуле ИМЯ_СУЩНОСТИ-ИМЯ_МЕТОДА). Правда есть одна сложность – в самом GenericDao есть методы, которые ненужно подменять, в частности это – метод load, загружающий объект из базы данных и метод save – сохраняющий объект в базе данных, соответственно.

Читать еще:  Удаление CTB-Locker.

IT блог

Современные методики разработки и тестирования ПО.

Динамический прокси на java и Ruby

Динамический прокси (Dynamic Proxy) оформился еще в Java 1.3.
Он позволяет использовать базовые возможности аспектно-ориентированного программирования без задействования, собственно, AOP фреймворка. Многие приложения и фреймворки используют такие возможности. Примерами могут быть spring и hibernate.
В этой заметке я расскажу как использовать динамический прокси в Java и поясню, как достичь того же в Ruby.

Java

Вероятно, вы видели в стеках имена классов вроде $Proxy[XX] — это и есть следы присутствия динамического прокси. Для того, чтобы создать таковой, вам надо указать, что вы хотите создать прокси для определенного интерфейса, и прокси будет создан в run-time. Вы определяете класс InvocationHandler и он будет вызван в случае вызова одного из методов прокси. В InvocationHandler передаются имя метода и его параметры, и с этими данными вы можете делать всё, что захотите. Обычно происходит вызов реализованного метода и какие-то утилитарные действия: логгирование, обработка исключений, управление транзакциями — типичные кандидаты при использование AOP.
Целевым объектом будет RealFoo.

В java это делается так (большая часть действий выполняется в invocation handler) :

Handler: Starting work for bar RealFoo: bar called, s = test string, i = 10 Printing stack trace for info Handler: Finishing work for bar java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1333) at ru.outofrange.test.RealFoo.bar(Main.java:53) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at ru.outofrange.test.MyInvocationHandler.invoke(Main.java:30) at ru.outofrange.test.service.$Proxy0.bar(Unknown Source) at ru.outofrange.test.Main.main(Main.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

Ruby

Ruby – это динамический язык программирования, который позволяет менять в run-time структуру классов и функций. В нем нет интерфейсов, он статически типизирован. Пример будет опираться на использование method_missing, который вызывается в runtime, если вызван несуществующий метод объекта. «Прокси» — это класс без методов (за исключением определенных в Object), который передает управление в целевой объект (target object).

class Proxy def initialize(targetObj) @targetObject=targetObj end def method_missing(m, *args, &block) puts “intercepted call for #” ret = @targetObject.send(m,*args, &block) puts “finished call for #” end end class Foo def bar(s,i) puts “Foo: bar called, s=#, i=#” puts “Foo: printing backtrace for info” # print back trace puts caller end end # run the example foo = Foo.new proxy = Proxy.new(foo) proxy.bar(“test string”,100)

Foo: bar called, s=test string, i=100 Foo: printing backtrace for info C:/Users/<>/workspace/ruby_scratch/Proxy.rb:9:in `send’ C:/Users/<>/workspace/ruby_scratch/Proxy.rb:9:in `method_missing’ C:/Users/<>/workspace/ruby_scratch/Proxy.rb:28 Proxy: finished call for bar

Вот, пожалуй, и всё. И в java, и в Ruby придется предпринимать дополнительные действия, если вы хотите обрабатывать исключения от целевых объектов. Пример на руби не показывает все возможные случаи использования (к примеру, замыкания) и может вызывать конфликты, если вы будете использовать такой подход в связке с библиотеками, которые меняют структуру класса. Эффект может быть достигнут и другими путями, например, переопределением метода целевого объекта. Мой пример приведен просто для ознакомления с прокси.

Related Posts

Допустим вы написали какой-то код без любимой IDE под рукой или решили изучить java исходники…

Часть 1: часть 1 Вопрос 7. На какую задержку нацеливать приложение? Ответ 7. Зависит от…

Перевод статьи: оригинал Вопрос 1. Что мы понимаем под терминами приложение реального времени, задержка и…

В чем разница между динамическим прокси JDK и CGLib?

В случае шаблона прокси-дизайна, в чем разница между JDK Dynamic Proxy и третьей стороной API генерации динамического кода, например CGLib?

В чем разница между использованием обоих подходов и когда следует выбирать один за другим?

Динамический прокси-сервер JDK может только прокси-интерфейсом (поэтому вашему целевому классу необходимо реализовать интерфейс, который затем также реализуется прокси-классом).

CGLIB (и javassist) может создавать прокси-сервер путем подкласса. В этом случае прокси становится подклассом целевого класса. Нет необходимости в интерфейсах.

Итак, Java Dynamic прокси могут прокси: public class Foo implements iFoo где CGLIB может прокси: public class Foo

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

Отличия в функциональности

Прокси JDK позволяют реализовать любой набор интерфейсов при создании подкласса Object . Любой интерфейсный метод, плюс Object::hashCode , Object::equals и Object::toString , затем направляется в InvocationHandler . Дополнительно реализован стандартный интерфейс библиотеки java.lang.reflect.Proxy .

cglib позволяет вам реализовать любой набор интерфейсов, в то же время создавая подклассы для любого не финального класса. Кроме того, методы могут быть переопределены по желанию, то есть не все неабстрактные методы должны быть перехвачены. Кроме того, существуют разные способы реализации метода. Он также предлагает класс InvocationHandler (в другом пакете), но он также позволяет вызывать супер-методы с использованием более сложных перехватчиков, например, MethodInterceptor . Кроме того, cglib может улучшить производительность за счет специализированных перехватов, таких как FixedValue . Однажды я написал обзор различных перехватчиков для cglib.

Различия в производительности

Прокси JDK реализованы довольно наивно только с одним диспетчером перехвата, InvocationHandler . Это требует отправки виртуального метода в реализацию, которая не всегда может быть встроена. Cglib позволяет создавать специализированный байт-код, который иногда может повысить производительность. Вот некоторые сравнения для реализации интерфейса с 18 методами-заглушками:

Время указывается в наносекундах со стандартным отклонением в фигурных скобках. Вы можете найти более подробную информацию о тесте в руководстве по Byte Buddy, где Byte Buddy является более современной альтернативой cglib. Также обратите внимание, что cglib больше не находится в активной разработке.

Динамический прокси: Динамические реализации интерфейсов во время выполнения с использованием API отражения JDK.

Пример: Spring использует динамические прокси для транзакций следующим образом:

Сгенерированный прокси-сервер входит в число bean. Он добавляет транснациональное поведение к bean. Здесь прокси генерирует динамически во время выполнения с использованием JDK Reflection API.

Когда приложение остановлено, прокси-сервер будет уничтожен, и мы будем иметь только интерфейс и bean в файловой системе.

В приведенном выше примере мы имеем интерфейс. Но в большинстве случаев реализация интерфейса не самая лучшая. Таким образом, bean не реализует интерфейс, в этом случае мы используем наследование:

Чтобы создать такие прокси, Spring использует стороннюю библиотеку с именем CGLib.

CGLib ( C ode G eneration Lib rary) построен поверх ASM, в основном используется генератор прокси расширения bean и добавляет поведение bean в прокси-методах.

Читать еще:  Новый механизм в Chrome 80 «сломает» многие сайты

Spring AOP использует динамические прокси JDK или CGLIB для создания прокси для данного целевого объекта. (Динамические прокси JDK предпочтительны, когда у вас есть выбор).

Если целевой объект для прокси реализует по крайней мере один интерфейс, то будет использоваться динамический прокси JDK. Все интерфейсы, реализованные целевым типом, будут проксированы. Если целевой объект не реализует никаких интерфейсов, будет создан прокси-сервер CGLIB.

Если вы хотите принудительно использовать прокси CGLIB (например, для прокси каждого метода, определенного для целевого объекта, а не только тех, которые реализованы его интерфейсами), вы можете сделать это. Тем не менее, есть несколько вопросов для рассмотрения:

окончательные методы не могут быть рекомендованы, так как они не могут быть переопределены.

Вам понадобятся двоичные файлы CGLIB 2 на вашем пути к классам, в то время как динамические прокси доступны с JDK. Spring автоматически предупредит вас, когда ему нужен CGLIB, а классы библиотеки CGLIB не найдены в пути к классам.

Конструктор вашего прокси-объекта будет вызываться дважды. Это естественное следствие модели прокси CGLIB, согласно которой для каждого объекта прокси создается подкласс. Для каждого экземпляра прокси создаются два объекта: фактический объект прокси и экземпляр подкласса, который реализует рекомендацию. Такое поведение не проявляется при использовании прокси-серверов JDK. Обычно, вызов конструктора прокси-типа дважды не является проблемой, поскольку обычно выполняются только присваивания, и в конструкторе не реализована реальная логика.

Динамические Прокси в Java

1. Вступление

Эта статья о Java динамических прокси – это один из основных механизмов прокси, доступных нам в языке.

Проще говоря, прокси – это фронты или оболочки, которые передают вызов функции через свои собственные средства (обычно в реальные методы) – потенциально добавляя некоторую функциональность.

Динамические прокси позволяют одному единственному классу с одним единственным методом обслуживать несколько вызовов методов для произвольных классов с произвольным числом методов. Динамический прокси можно рассматривать как своего рода Facade , но тот, который может претендовать на реализацию любого интерфейса. Под прикрытием он направляет все вызовы метода одному обработчику – методу invoke () .

Хотя это не инструмент, предназначенный для повседневных задач программирования, динамические прокси могут быть весьма полезны для разработчиков фреймворков. Он также может использоваться в тех случаях, когда конкретные реализации классов не будут известны до времени выполнения.

Эта функция встроена в стандартный JDK, поэтому никаких дополнительных зависимостей не требуется.

2. Обработчик вызовов

Давайте создадим простой прокси-сервер, который на самом деле ничего не делает, кроме печати того, какой метод был запрошен для вызова, и возвращает жестко запрограммированное число.

Сначала нам нужно создать подтип java.lang.reflect.InvocationHandler :

Здесь мы определили простой прокси, который регистрирует, какой метод был вызван, и возвращает 42.

3. Создание экземпляра прокси

Экземпляр прокси, обслуживаемый только что определенным нами обработчиком вызова, создается с помощью вызова метода фабрики класса java.lang.reflect.Proxy :

Когда у нас есть экземпляр прокси, мы можем вызывать его методы интерфейса как обычно:

Как и ожидалось, сообщение о вызываемом методе put () выводится в файл журнала.

4. Обработчик вызовов через лямбда-выражения

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

Здесь мы определили обработчик, который возвращает 42 для всех операций get и выбрасывает UnsupportedOperationException для всего остального.

Он вызывается точно так же:

5. Пример динамического прокси-сервера Timing

Давайте рассмотрим один потенциальный реальный сценарий для динамических прокси

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

Впоследствии этот прокси может использоваться на объектах различных типов:

Здесь мы проксировали карту и последовательность символов (String).

Вызовы прокси-методов делегируют обернутый объект, а также создают операторы логирования:

6. Заключение

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

Как всегда, код в примерах можно найти по адресу over на GitHub .

Что такое .Net эквивалент динамических Прокси Java?

В java можно динамически реализовать интерфейс с помощью динамического прокси, что-то вроде этого:

Есть ли эквивалент в .Net?

5 Ответов

Наиболее широко используется динамический прокси-сервер проекта Castle, который также используется несколькими (или, по крайней мере, 1) насмешливыми фреймворками. Имейте в виду, что методы (и подслащенные методы, такие как свойства) не являются виртуальными по умолчанию в dotnet, так что это может создать некоторые головные боли, Если вы не ожидали этого в своем дизайне класса.

Есть несколько библиотек, которые реализуют это в .NET. Вот список из них, с эталоном.

Да. Является производным от абстрактного класса RealProxy .

Взгляните также на PoshSharp (AOP framework for .NET) – он может делать подобные вещи, но во время компиляции. Возможно, вы предпочтете подход, который он поддерживает.

Прямого эквивалента нет, но смотрите, как сделать динамические прокси в C# для некоторых обходных путей:

Фон: динамический прокси-сервер динамически генерирует класс по адресу среда выполнения, соответствующая определенному интерфейс, проксирующий все вызовы к один метод ‘generic’.

Ранее Стелсми спросила, Так ли это это можно сделать в .NET (это a стандартная часть Java). Видя, как это уже второй раз я говорю об этом через столько же дней, я думаю, это стоит того ведение блога.

Похожие вопросы:

Что такое эквивалент C# для классов Java BufferedInputStream и BufferedOutputStream?

пожалуйста, не могли бы вы сказать мне, что такое эквивалент .NET System.TypeCode перечисление находится в Java и как его получить? Я ничего не могу найти в классе Class в документах, которые.

Я конвертирую программу Java в C#. Мне интересно, что это такое .NET эквивалент Java InterruptedException ?

Что такое эквивалент java ManualResetEvent ?

Что такое эквивалент a Java applet in .NET? Это Silverlight? Java applet все еще широко используется?

Я-парень .NET, но недавно был помещен в проект Java. Существует ли java эквивалент корпоративной библиотеки .NET? Более конкретно, мне нужен только менеджер конфигурации, помощник по доступу к.

Что такое Java эквивалент .NET Recycle для веб-приложений в IIS. Это происходит при использовании Java на машине linux за пределами IIS. Это просто, чтобы остановить и запустить приложение?

Что такое .NET эквивалент java.lang.StringBuffer ?

Какова стоимость использования динамических прокси? Я не хочу загромождать свой проект реализациями интерфейса, поэтому я рассматриваю возможность использования динамических прокси, созданных.

Что такое эквивалент Java .Интерфейс NET IEquatable?

голоса
Рейтинг статьи
Ссылка на основную публикацию
Статьи c упоминанием слов: