Класс 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:и следующая операция приведения типов будет успешной (вместо выбросаproxy instanceof FooClassCastException):(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.
- Since:
- 1.3
- See Also:
Краткое описание полей
| Модификатор и тип | Поле | Описание |
|---|---|---|
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; - любой из заданных интерфейсов прокси является не-общедоступным и класс вызывающего кода не находится в том же runtime пакете, что и не-общедоступный интерфейс, и вызов
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, 2021, 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/17/docs/api/java.base/java/lang/reflect/Proxy.html