Класс Proxy
- java.lang.Object
-
- java.lang.reflect.Proxy
- Все реализованные интерфейсы:
- Serializable
public class Proxy extends Object implements Serializable
Proxy предоставляет статические методы для создания динамических классов-прокси и экземпляров, а также является суперклассом всех динамических классов-прокси, созданных этими методами.
Для создания прокси для некоторого интерфейса Foo:
InvocationHandler handler = new MyInvocationHandler(...);
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler); или проще: Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },
handler); Динамический класс-прокси (в дальнейшем просто класс-прокси) — это класс, реализующий список интерфейсов, указанных во время выполнения при создании класса, с поведением, описанным ниже. Интерфейс прокси — это такой интерфейс, который реализуется классом-прокси. Экземпляр прокси — это экземпляр класса-прокси. Каждый экземпляр прокси имеет связанный с ним объект обработчика вызовов, который реализует интерфейс InvocationHandler. Вызов метода на экземпляре прокси через один из его интерфейсов прокси будет перенаправлен на метод invoke обработчика вызовов экземпляра, передавая экземпляр прокси, объект java.lang.reflect.Method , определяющий метод, который был вызван, и массив типа Object содержащий аргументы. Обработчик вызовов обрабатывает закодированный вызов метода соответствующим образом, и результат, который он возвращает, будет возвращен как результат вызова метода на экземпляре прокси.
Класс-прокси имеет следующие свойства:
- Классы-прокси являются общедоступными, final и не абстрактными, если все интерфейсы прокси являются общедоступными.
- Классы-прокси являются не общедоступными, final и не абстрактными, если любой из интерфейсов прокси является неопределённым.
- Неполное имя класса-прокси не определено. Однако пространство имён классов, начинающихся со строки
"$Proxy"должно быть зарезервировано для классов-прокси. - Класс-прокси расширяет
java.lang.reflect.Proxy. - Класс-прокси реализует ровно те интерфейсы, которые были указаны при его создании, в том же порядке.
- Если класс-прокси реализует не общедоступный интерфейс, то он будет определён в той же папке, что и этот интерфейс. В противном случае, пакет класса-прокси также не определён. Обратите внимание, что уплотнение пакетов не помешает классу-прокси быть успешно определённым в определённом пакете во время выполнения, а также не помешают классы, уже определённые тем же загрузчиком классов и тем же пакетом с конкретными подписями.
- Поскольку класс-прокси реализует все интерфейсы, указанные при его создании, вызов
getInterfacesна его объектеClassвернёт массив, содержащий тот же список интерфейсов (в порядке, указанном при его создании), вызовgetMethodsна его объектеClassвернёт массив объектовMethod, которые включают все методы в этих интерфейсах, и вызовgetMethodнайдёт методы в интерфейсах прокси, как и ожидалось. - Метод
Proxy.isProxyClassвернёт true, если ему передан класс-прокси — класс, возвращаемыйProxy.getProxyClassили класс объекта, возвращаемогоProxy.newProxyInstance, и false в противном случае. - Домен защиты
java.security.ProtectionDomainкласса-прокси такой же, как и у системных классов, загруженных загрузчиком класса загрузки, таких какjava.lang.Object, потому что код класса-прокси генерируется надёжным системным кодом. Этот домен защиты обычно будет предоставленjava.security.AllPermission. - Каждый класс-прокси имеет один общедоступный конструктор, который принимает один аргумент — реализацию интерфейса
InvocationHandler, для установки обработчика вызовов для экземпляра прокси. Вместо того, чтобы использовать API рефлексии для доступа к общедоступному конструктору, экземпляр прокси также может быть создан путём вызова методаProxy.newProxyInstance, который объединяет действия вызоваProxy.getProxyClassс вызовом конструктора с обработчиком вызовов.
Экземпляр прокси имеет следующие свойства:
- Учитывая экземпляр прокси
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.
Методы, дублирующиеся в нескольких интерфейсах прокси
Когда два или более интерфейса класса-прокси содержат метод с одинаковым именем и сигнатурой параметра, порядок интерфейсов класса-прокси становится существенным. При вызове такого метода-дубликата на экземпляре прокси, объект Method , переданный обработчику вызовов, не обязательно будет тем, чей объявляющий класс присваивается типу ссылки интерфейса, через который был вызван метод прокси. Это ограничение существует, потому что соответствующая реализация метода в сгенерированном классе-прокси не может определить, через какой интерфейс он был вызван. Поэтому, когда метод-дубликат вызывается на экземпляре прокси, объект Method для метода в главном интерфейсе, содержащем метод (либо напрямую, либо через супер-интерфейс), в списке интерфейсов класса-прокси передаётся обработчику вызовов invoke методу, независимо от типа ссылки, через который произошёл вызов метода.
Если интерфейс прокси содержит метод с таким же именем и сигнатурой параметра, как методы hashCode, equals, или toString класса java.lang.Object, при вызове такого метода на экземпляре прокси, объект Method , переданный обработчику вызовов, будет иметь java.lang.Object в качестве объявляющего класса. Другими словами, общедоступные, не-final методы java.lang.Object логически предшествуют всем интерфейсам прокси для определения, какой объект Method передать обработчику вызовов.
Также обратите внимание, что при перенаправлении дублирующегося метода обработчику вызовов, метод invoke может выбрасывать только типы проверенных исключений, которые присваиваются одному из типов исключений в throws пункте метода во всех интерфейсах прокси, через которые он может быть вызван. Если метод invoke выбрасывает проверенное исключение, которое не присваивается ни одному из типов исключений, объявленных методом в одном из интерфейсов прокси, через которые он может быть вызван, то не проверенное исключение UndeclaredThrowableException будет выброшено вызовом на экземпляре прокси. Это ограничение означает, что не все типы исключений, возвращённые вызовом getExceptionTypes на объекте Method , переданном методу invoke, могут быть успешно выброшены методом invoke.
- С момента:
- 1.3
- См. также:
-
InvocationHandler, Сериализованная форма
Поля
| Модификатор и тип | Поле и описание |
|---|---|
protected InvocationHandler |
h обработчик вызовов для данного экземпляра прокси. |
Конструкторы
| Модификатор | Конструктор и описание |
|---|---|
protected |
Proxy(InvocationHandler h) Создаёт новый экземпляр |
Методы
| Модификатор и тип | Метод и описание |
|---|---|
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
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException Возвращает объект java.lang.Class для класса прокси, заданного загрузчиком классов и массивом интерфейсов. Класс прокси будет определён заданным загрузчиком классов и будет реализовывать все указанные интерфейсы. Если какой-либо из указанных интерфейсов является непубличным, то класс прокси также будет непубличным. Если класс прокси для такой же комбинации интерфейсов уже был определён загрузчиком классов, то будет возвращён существующий класс прокси; в противном случае, класс прокси для этих интерфейсов будет сгенерирован динамически и определён загрузчиком классов.
Существует несколько ограничений на параметры, которые могут быть переданы в Proxy.getProxyClass:
- Все объекты
Classв массивеinterfacesдолжны представлять интерфейсы, а не классы или примитивные типы. - Никакие два элемента в массиве
interfacesне должны ссылаться на идентичные объектыClass. - Все типы интерфейсов должны быть видимы по имени через указанный загрузчик классов. Другими словами, для загрузчика классов
clи каждого интерфейсаi, следующее выражение должно быть истинным:Class.forName(i.getName(), false, cl) == i
- Все непубличные интерфейсы должны быть в одном пакете; иначе невозможно, чтобы класс прокси реализовывал все интерфейсы, независимо от того, в каком пакете он определён.
- Для любой совокупности методов членов указанных интерфейсов, имеющих одинаковый сигнатур:
- Если тип возвращаемого значения любого из методов — примитивный тип или void, то все методы должны иметь тот же тип возвращаемого значения.
- В противном случае, тип возвращаемого значения одного из методов должен быть приводимым к типам возвращаемого значения всех остальных методов.
- Полученный класс прокси не должен превышать никаких ограничений, накладываемых на классы виртуальной машиной. Например, ВМ может ограничить количество интерфейсов, которые может реализовать класс, до 65535; в этом случае размер массива
interfacesне должен превышать 65535.
Если какое-либо из этих ограничений нарушено, Proxy.getProxyClass выбросит исключение IllegalArgumentException. Если аргумент interfaces или любой из его элементов является null, будет выброшено исключение NullPointerException.
Обратите внимание, что порядок указанных интерфейсов прокси важен: два запроса на класс прокси с одинаковой комбинацией интерфейсов, но в разном порядке, приведут к двум различным классам прокси.
- Параметры:
-
loader- загрузчик классов для определения класса прокси -
interfaces- список интерфейсов для реализации классом прокси - Возвращаемое значение:
- класс прокси, который определён в указанном загрузчике классов и реализует указанные интерфейсы
- Исключения:
-
IllegalArgumentException- если какое-либо из ограничений на параметры, которые могут быть переданы вgetProxyClass, нарушено -
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)
throws IllegalArgumentException Возвращает экземпляр класса прокси для указанных интерфейсов, который перенаправляет вызовы методов заданному обработчику вызовов.
Proxy.newProxyInstance выбросит исключение IllegalArgumentException по тем же причинам, что и Proxy.getProxyClass.
- Параметры:
-
loader- загрузчик классов для определения класса прокси -
interfaces- список интерфейсов для реализации классом прокси -
h- обработчик вызовов для перенаправления вызовов методов - Возвращаемое значение:
- экземпляр прокси с заданным обработчиком вызовов класса прокси, определённого указанным загрузчиком классов и реализующего указанные интерфейсы
- Исключения:
-
IllegalArgumentException- если какое-либо из ограничений на параметры, которые могут быть переданы вgetProxyClass, нарушено -
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 тогда и только тогда, когда указанный класс был динамически сгенерирован как класс прокси с помощью метода getProxyClass или метода newProxyInstance.
Надёжность этого метода важна для возможности использования его для принятия решений о безопасности, поэтому его реализация не должна просто проверять, расширяет ли рассматриваемый класс 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.