Класс 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класса-прокси такой же, как у системных классов, загруженных загрузчиком базового класса, например,java.lang.Object, так как код класса-прокси генерируется надёжным системным кодом. Этот домен защиты обычно будет предоставленjava.security.AllPermission. - Метод
Proxy.isProxyClassможно использовать для определения, является ли данный класс классом-прокси.
Экземпляр-прокси имеет следующие свойства:
- Для экземпляра-прокси
proxyи одного из интерфейсовFoo, реализованных его классом-прокси, следующее выражение вернёт true:, и следующая операция приведения типов будет успешной (вместо выброса исключения %%%CODE_BLOCK_26%%):proxy instanceof Foo(Foo) proxy - Каждый экземпляр-прокси имеет связанный с ним обработчик вызовов, тот, который был передан в его конструктор. Статический метод
Proxy.getInvocationHandlerвернёт обработчик вызовов, связанный с экземпляром-прокси, переданным в качестве аргумента. - Вызов метода интерфейса на экземпляре-прокси будет закодирован и перенаправлен на метод
invokeобработчика вызовов, как описано в документации к этому методу. - Прокси-интерфейс может определять метод по умолчанию или наследоваться от него от своего суперинтерфейса напрямую или косвенно. Обработчик вызовов может вызывать метод по умолчанию прокси-интерфейса, вызывая
InvocationHandler::invokeDefault. - Вызов методов
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), определяется следующим образом: - Если все прокси-интерфейсы находятся в пакетах экспортированных или открытых:
- Если все прокси-интерфейсы публичные, то класс-прокси публичный в безусловно экспортированном, но не открытом пакете. Название пакета и модуля не указано.
- Если хотя бы один из всех прокси-интерфейсов непубличный, то класс-прокси непубличный в пакете и модуле непубличных интерфейсов. Все непубличные интерфейсы должны находиться в одном пакете и модуле; в противном случае их создание прокси-объекта невозможно.
- Если хотя бы один прокси-интерфейс находится в пакете, который не экспортирован и не открыт:
- Если все прокси-интерфейсы публичные, то класс-прокси публичный в неэкспортированном, неокрытом пакете динамического модуля. Названия пакета и модуля не указаны.
- Если хотя бы один из всех прокси-интерфейсов непубличный, то класс-прокси непубличный в пакете и модуле непубличных интерфейсов. Все непубличные интерфейсы должны находиться в одном пакете и модуле; в противном случае их создание прокси-объекта невозможно.
Обратите внимание, что если прокси-интерфейсы с различными уровнями доступности — например, экспортированный публичный интерфейс и неэкспортированный непубличный интерфейс — создаются в одном экземпляре прокси-класса, то доступность класса-прокси определяется наименее доступным прокси-интерфейсом.
Обратите внимание, что произвольный код может получить доступ к классу-прокси в открытом пакете с помощью 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 может выбрасывать только типы исключений, которые подчинённы одному из типов исключений в throws пункте метода во всех прокси-интерфейсах, через которые он может быть вызван. Если метод invoke выбрасывает проверенное исключение, которое не подчинённо ни одному из типов исключений, объявленных в методе в одном из прокси-интерфейсов, через который он может быть вызван, то необработанное исключение UndeclaredThrowableException будет выбрасываться при вызове на экземпляре прокси. Это ограничение означает, что не все типы исключений, возвращаемые вызовом getExceptionTypes на объекте Method, переданном методу invoke, могут обязательно быть успешно выброшены методом invoke.
- С момента:
- 1.3
- См. также:
Краткое описание полей
| Модификатор и тип | Поле | Описание |
|---|---|---|
protected InvocationHandler |
h |
обработчик вызовов для данного экземпляра прокси. |
Краткое описание конструкторов
| Модификатор | Конструктор | Описание |
|---|---|---|
protected |
Создает новый экземпляр Proxy из подкласса (обычно, динамический класс прокси) с указанным значением для обработчика вызовов. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
static InvocationHandler |
getInvocationHandler |
Возвращает обработчик вызовов для указанного экземпляра прокси. |
static Class |
getProxyClass |
Устарело. Классы прокси, сгенерированные в именованном модуле, инкапсулированы и недоступны коду вне этого модуля. |
static boolean |
isProxyClass |
Возвращает true, если данный класс является классом прокси. |
static Object |
newProxyInstance |
Возвращает экземпляр прокси для указанных интерфейсов, который перенаправляет вызовы методов на указанный обработчик вызовов. |
Подробное описание полей
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)
- Примечание об реализации:
- Надёжность этого метода важна для возможности использования его для принятия решений о безопасности, поэтому его реализация не должна просто проверять, расширяет ли проверяемый класс
Proxy. - Параметры:
-
cl- класс для проверки - Возвращает:
-
trueесли класс является классом прокси иfalseв противном случае - Исключения:
-
NullPointerException- еслиclявляетсяnull
getInvocationHandler
public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException
- Параметры:
-
proxy- экземпляр прокси для возвращения обработчика вызовов - Возвращает:
- обработчик вызовов для экземпляра прокси
- Исключения:
-
IllegalArgumentException- если аргумент не является экземпляром прокси -
SecurityException- если менеджер безопасности, s, присутствует и загрузчик класса вызывающего не является тем же или предком загрузчика классов для обработчика вызовов и вызовs.checkPackageAccess()отказывается в доступе к классу обработчика вызовов.
© 1993, 2023, 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/21/docs/api/java.base/java/lang/reflect/Proxy.html