Класс Proxy

Все реализованные интерфейсы:
Serializable
public class Proxy
extends Object
implements Serializable

Proxy предоставляет статические методы для создания объектов, которые ведут себя как экземпляры интерфейсов, но позволяют настраивать вызовы методов. Чтобы создать прокси-экземпляр для некоторого интерфейса Foo:

InvocationHandler handler = new MyInvocationHandler(...);
     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class<?>[] { Foo.class },
                                          handler);

Класс-прокси — это класс, созданный во время выполнения, который реализует указанный список интерфейсов, известных как прокси-интерфейсы. Экземпляр-прокси — это экземпляр класса-прокси. Каждый экземпляр-прокси имеет связанный объект обработчика вызовов, который реализует интерфейс InvocationHandler. Вызов метода на экземпляре-прокси через один из его прокси-интерфейсов будет перенаправлен на метод invoke объекта-обработчика вызовов экземпляра, передавая экземпляр-прокси, объект java.lang.reflect.Method , идентифицирующий метод, который был вызван, и массив типа Object , содержащий аргументы. Обработчик вызовов обрабатывает закодированный вызов метода соответствующим образом, и возвращаемое им значение будет возвращено как результат вызова метода на экземпляре-прокси.

Класс-прокси имеет следующие свойства:

  • Неопределенное имя класса-прокси без квалификатора. Пространство имён классов, начинающихся со строки "$Proxy", должно быть зарезервировано для классов-прокси.
  • Пакет и модуль, в которых определен класс-прокси, указаны ниже.
  • Класс-прокси является неизменяемым и не абстрактным.
  • Класс-прокси расширяет java.lang.reflect.Proxy.
  • Класс-прокси реализует ровно те интерфейсы, которые были указаны при его создании, в том же порядке. Вызов getInterfaces на его объекте Class вернёт массив, содержащий тот же список интерфейсов (в указанном порядке при его создании), вызов getMethods на его объекте Class вернёт массив объектов Method , включающий все методы в этих интерфейсах, и вызов getMethod найдёт методы в прокси-интерфейсах, как ожидается.
  • ProtectionDomain класса-прокси такой же, как у системных классов, загруженных загрузчиком bootstrap-классов, таких как java.lang.Object, потому что код для класса-прокси генерируется надёжным системным кодом. Эта область защиты обычно получает java.security.AllPermission.
  • Метод Proxy.isProxyClass может использоваться для определения того, является ли данный класс классом-прокси.

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

  • Для данного экземпляра-прокси proxy и одного из интерфейсов Foo, реализованных его классом-прокси, следующее выражение вернёт true:
    proxy instanceof Foo
    , а следующее преобразование типа будет успешно (вместо выброса исключения ClassCastException):
    (Foo) proxy
  • Каждый экземпляр-прокси имеет связанный обработчик вызовов, тот, который был передан в его конструктор. Статический метод Proxy.getInvocationHandler вернёт обработчик вызовов, связанный с экземпляром-прокси, переданным в качестве аргумента.
  • Вызов метода интерфейса на экземпляре-прокси будет закодирован и передан в метод invoke обработчика вызовов, как описано в документации к этому методу.
  • Вызов методов hashCode, equals, или toString , объявленных в java.lang.Object, на экземпляре-прокси будет закодирован и передан в метод invoke обработчика вызовов аналогично тому, как кодируются и передаются вызовы методов интерфейса, как описано выше. Класс-объявитель объекта Method , переданного в invoke , будет java.lang.Object. Другие публичные методы экземпляра-прокси, унаследованные от java.lang.Object , не переопределяются классом-прокси, поэтому вызовы этих методов будут вести себя так же, как и для экземпляров java.lang.Object.

Принадлежность пакета и модуля к классу-прокси

Пакет и модуль, к которым принадлежит класс-прокси, выбираются таким образом, чтобы доступность класса-прокси соответствовала доступности прокси-интерфейсов. Конкретно, принадлежность пакета и модуля к классу-прокси, определённому методами getProxyClass(ClassLoader, Class[]) или newProxyInstance(ClassLoader, Class[], InvocationHandler), определяется следующим образом:
  1. Если все прокси-интерфейсы находятся в пакетах, отмеченных как экспортируемые или открытые:
    1. Если все прокси-интерфейсы являются публичными, то класс-прокси является публичным в пакете, экспортированном беспредельным модулем (unnamed module) указанного загрузчика. Имя пакета не определено.
    2. Если хотя бы один из всех прокси-интерфейсов является непубличным, то класс-прокси является непубличным в пакете и модуле непубличных интерфейсов. Все непубличные интерфейсы должны находиться в одном пакете и модуле; в противном случае создание прокси невозможно. невозможно.
  2. Если хотя бы один прокси-интерфейс находится в пакете, который неэкспортируемый и неоткрытый:
    1. Если все прокси-интерфейсы являются публичными, то класс-прокси является публичным в неэкспортируемом, неооткрытом пакете динамического модуля. Имена пакета и модуля не определены.
    2. Если хотя бы один из всех прокси-интерфейсов является непубличным, то класс-прокси является непубличным в пакете и модуле непубличных интерфейсов. Все непубличные интерфейсы должны находиться в одном пакете и модуле; в противном случае создание прокси невозможно. невозможно.

Обратите внимание, что если прокси-интерфейсы с различными уровнями доступа — например, экспортируемый публичный интерфейс и неэкспортируемый непубличный интерфейс — проксируются одним экземпляром, то доступность класса-прокси определяется наименее доступным прокси-интерфейсом.

Обратите внимание, что произвольный код может получить доступ к классу-прокси в открытом пакете с помощью setAccessible, тогда как к классу-прокси в неооткрытом пакете код за пределами модуля класса-прокси никогда не получит доступа.

В этом описании «неэкспортируемый пакет» относится к пакету, который не экспортируется во все модули, а «неооткрытый пакет» — к пакету, который не открыт для всех модулей. В частности, эти термины относятся к пакету, который либо не экспортируется/открывается содержащим его модулем, либо экспортируется/открывается в квалифицированной форме содержащим его модулем.

Динамические модули

Динамический модуль — это именованный модуль, генерируемый во время выполнения. Класс-прокси, определённый в динамическом модуле, изолирован и недоступен ни для какого модуля. Вызов Constructor.newInstance(Object...) на классе-прокси в динамическом модуле выбросит исключение IllegalAccessException; вместо этого следует использовать метод Proxy.newProxyInstance.

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

Методы, дублирующиеся в нескольких прокси-интерфейсах

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

Если прокси-интерфейс содержит метод с таким же именем и сигнатурой параметров, как методы hashCode, equals, или toString класса java.lang.Object, когда такой метод вызывается на экземпляре-прокси, объект Method , переданный обработчику вызовов, будет иметь класс-объявитель java.lang.Object. Другими словами, публичные, нестатические методы java.lang.Object логически предшествуют всем прокси-интерфейсам для определения того, какой объект Method передать обработчику вызовов.

Обратите также внимание, что когда дублирующийся метод передаётся обработчику вызовов, метод invoke может выбрасывать только типы исключений checked, приводимые к одному из типов исключений в пункте throws метода во всех прокси-интерфейсах, через которые он может быть вызван. Если метод invoke выбрасывает исключение checked, которое не приводимо ни к одному из типов исключений, объявленных в методе одного из прокси-интерфейсов, через который он может быть вызван, то необработанное исключение UndeclaredThrowableException будет выброшено в вызове на экземпляре-прокси. Это ограничение означает, что не все типы исключений, возвращённые при вызове getExceptionTypes на объекте Method , переданном методу invoke , обязательно могут быть успешно выброшены методом invoke.

С момента:
1.3
См. также:
InvocationHandler, Сериализованная форма

Поля

Модификатор и тип Поле Описание
protected InvocationHandler h

обработчик вызовов для этого экземпляра прокси.

Конструкторы

Модификатор Конструктор Описание
protected Proxy​(InvocationHandler h)

Создает новый экземпляр Proxy из подкласса (обычно, динамического класса прокси) со значением для его обработчика вызовов.

Методы

Модификатор и тип Метод Описание
static InvocationHandler getInvocationHandler​(Object proxy)

Возвращает обработчик вызовов для указанного экземпляра прокси.

static Class<?> getProxyClass​(ClassLoader loader, Class<?>... interfaces)

Устарело.

Классы прокси, сгенерированные в именованном модуле, инкапсулированы и недоступны коду за пределами этого модуля.

static boolean isProxyClass​(Class<?> cl)

Возвращает true, если данный класс является классом прокси.

static Object newProxyInstance​(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

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

Методы, объявленные в классе java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Поля

h

protected InvocationHandler h

обработчик вызовов для этого экземпляра прокси.

Конструкторы

Proxy

protected Proxy(InvocationHandler h)

Создает новый экземпляр Proxy из подкласса (обычно, динамического класса прокси) со значением для его обработчика вызовов.

Параметры:
h - обработчик вызовов для этого экземпляра прокси
Исключения:
NullPointerException - если данный обработчик вызовов, h, является null.

Методы

getProxyClass

@Deprecated
public static Class<?> getProxyClass(ClassLoader loader,
                                     Class<?>... interfaces)
                              throws IllegalArgumentException
Устарело.
Классы прокси, сгенерированные в именованном модуле, инкапсулированы и недоступны коду за пределами этого модуля. Constructor.newInstance будет выбрасывать IllegalAccessException при вызове на недоступном классе прокси. Используйте newProxyInstance(ClassLoader, Class[], InvocationHandler) для создания экземпляра прокси вместо этого.

Возвращает объект java.lang.Class для класса прокси, заданного загрузчиком классов и массивом интерфейсов. Класс прокси будет определен заданным загрузчиком классов и будет реализовывать все указанные интерфейсы. Если любой из указанных интерфейсов является непубличным, класс прокси также будет непубличным. Если класс прокси для той же комбинации интерфейсов уже определен загрузчиком классов, то будет возвращен существующий класс прокси; в противном случае динамически сгенерирован класс прокси для этих интерфейсов и определен загрузчиком классов.

Параметры:
loader - загрузчик классов для определения класса прокси
interfaces - список интерфейсов для реализации классом прокси
Возвращает:
класс прокси, который определен в указанном загрузчике классов и реализует указанные интерфейсы
Исключения:
IllegalArgumentException - если какие-либо из ограничений на параметры нарушены
SecurityException - если менеджер безопасности, s, присутствует и выполняется любое из следующих условий:
  • указанный loader является null и загрузчик класса вызывающего кода не является null и вызов s.checkPermission с RuntimePermission("getClassLoader") разрешением отказывает доступ.
  • для каждого интерфейса прокси, intf, загрузчик класса вызывающего кода не является тем же или предком загрузчика класса для intf и вызов s.checkPackageAccess() отказывает доступ к intf.
NullPointerException - если массив interfaces аргументов или любой из его элементов являются null
См. также:
Членство пакета и модуля класса прокси

newProxyInstance

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

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

IllegalArgumentException будет выброшен, если любое из следующих ограничений нарушено:

  • Все Class объекты в заданном interfaces массиве должны представлять интерфейсы, а не классы или примитивные типы.
  • Ни два элемента в interfaces массиве не могут ссылаться на идентичные Class объекты.
  • Все типы интерфейсов должны быть видимыми по имени через указанный загрузчик классов. Другими словами, для загрузчика классов cl и каждого интерфейса i, следующее выражение должно быть истинным:

    Class.forName(i.getName(), false, cl) == i

  • Все типы, на которые ссылаются все публичные сигнатуры методов указанных интерфейсов и унаследованные их суперинтерфейсами, должны быть видимыми по имени через указанный загрузчик классов.
  • Все непубличные интерфейсы должны быть в одном пакете и модуле, определенном указанным загрузчиком классов, и модуль непубличных интерфейсов может получить доступ ко всем типам интерфейсов; в противном случае для класса прокси не будет возможно реализовать все интерфейсы, независимо от того, в каком пакете он определен.
  • Для любого набора методов-членов указанных интерфейсов, имеющих один и тот же сигнатуру:
    • Если тип возвращаемого значения любого из методов является примитивным типом или void, то все методы должны иметь тот же тип возвращаемого значения.
    • В противном случае один из методов должен иметь тип возвращаемого значения, который может быть присвоен всем типам возвращаемых значений остальных методов.
  • Полученный класс прокси не должен превышать никаких ограничений, наложенных на классы виртуальной машиной. Например, ВМ может ограничить количество интерфейсов, которые может реализовать класс, до 65535; в этом случае размер interfaces массива не должен превышать 65535.

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

Параметры:
loader - загрузчик классов для определения класса прокси
interfaces - список интерфейсов для реализации классом прокси
h - обработчик вызовов для перенаправления вызовов методов
Возвращает:
экземпляр прокси с указанным обработчиком вызовов класса прокси, который определен заданным загрузчиком классов и реализует указанные интерфейсы
Исключения:
IllegalArgumentException - если какие-либо из ограничений на параметры нарушены
SecurityException - если менеджер безопасности, s, присутствует и выполняется любое из следующих условий:
  • указанный loader является null и загрузчик класса вызывающего кода не является null и вызов s.checkPermission с RuntimePermission("getClassLoader") разрешением отказывает доступ;
  • для каждого интерфейса прокси, intf, загрузчик класса вызывающего кода не является тем же или предком загрузчика класса для intf и вызов s.checkPackageAccess() отказывает доступ к intf;
  • любой из указанных интерфейсов прокси является непубличным и класс вызывающего кода не находится в том же пакете что и непубличный интерфейс, и вызов s.checkPermission с ReflectPermission("newProxyInPackage.{package name}") разрешением отказывает доступ.
NullPointerException - если массив interfaces аргументов или любой из его элементов являются null, или если обработчик вызовов, h, является null
См. также:
Членство пакета и модуля класса прокси

isProxyClass

public static boolean isProxyClass(Class<?> cl)

Возвращает true, если данный класс является классом прокси.

Примечание к реализации:
Надежность этого метода важна для возможности использования его для принятия решений о безопасности, поэтому его реализация не должна просто проверять, расширяет ли рассматриваемый класс Proxy.
Параметры:
cl - класс для проверки
Возвращает:
true если класс является классом прокси и false в противном случае
Исключения:
NullPointerException - если cl является null

getInvocationHandler

public static InvocationHandler getInvocationHandler(Object proxy)
                                              throws IllegalArgumentException

Возвращает обработчик вызовов для указанного экземпляра прокси.

Параметры:
proxy - экземпляр прокси для возврата обработчика вызовов
Возвращает:
обработчик вызовов для экземпляра прокси
Исключения:
IllegalArgumentException - если аргумент не является экземпляром прокси
SecurityException - если менеджер безопасности, s, присутствует и загрузчик класса вызывающего кода не является тем же или предком загрузчика класса для обработчика вызовов и вызов s.checkPackageAccess() отказывает доступ к классу обработчика вызовов.

© 1993, 2020, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/Proxy.html

Spec-Zone .ru
спецификации, руководства, описания, API