Spec-Zone .ru
спецификации, руководства, описания, API
|
Динамический прокси class является class, который реализует список интерфейсов, определенных во времени выполнения так, что вызов метода через один из интерфейсов на экземпляре class, будет закодирован и диспетчеризирован другому объекту через универсальный интерфейс. Таким образом динамический прокси class может использоваться, чтобы создать безопасный с точки зрения типов объект прокси для списка интерфейсов, не требуя предварительной генерации прокси class, такой как с инструментами времени компиляции. Вызовы метода на экземпляре динамического прокси, class диспетчеризируется единственному методу в обработчике вызова экземпляра, и они кодируются с a java.lang.reflect.Method
объект, идентифицирующий метод, который был вызван и массив типа Object
содержа параметры.
Динамические прокси-классы полезны для приложения или библиотеки, которая должна обеспечить безопасный с точки зрения типов отражающий, диспетчеризируют вызовов на объектах тот существующий интерфейс API. Например, приложение может использовать динамический прокси class, чтобы создать объект, который реализует многократные интерфейсы слушателя случайного события - интерфейсы, которые расширяются java.util.EventListener
- обработать множество событий различных типов универсальным способом, такой как, регистрируя все такие события к файлу.
Динамическим class прокси (просто называемый прокси class ниже) является class, который реализует список интерфейсов, определенных во времени выполнения, когда class создается.
Интерфейс прокси является таким интерфейсом, который реализуется прокси class.
Экземпляр прокси является экземпляром прокси class.
Прокси-классы, так же как экземпляры их, создаются, используя статические методы class java.lang.reflect.Proxy.
Proxy.getProxyClass
метод возвращается java.lang.Class
объект для прокси class, данный загрузчик class и массив интерфейсов. Прокси class будет определен в указанном загрузчике class и реализует все предоставленные интерфейсы. Если прокси, class для той же самой перестановки интерфейсов был уже определен в загрузчике class, то существующий прокси class будет возвращен; иначе, прокси class для тех интерфейсов будет сгенерирован динамически и определен в загрузчике class.
Есть несколько ограничений на параметры, к которым можно передать Proxy.getProxyClass
:
Class
объекты в interfaces
массив должен представить интерфейсы, не классы или типы примитивов.interfaces
массив может обратиться к идентичному Class
объекты.cl
и каждый интерфейс i
, следующее выражение должно быть истиной: Class.forName(i.getName(), false, cl) == i
interfaces
массив не должен превысить 65535.Если какое-либо из этих ограничений нарушается, Proxy.getProxyClass
бросит IllegalArgumentException
. Если interfaces
параметр массива или любой из его элементов null
, a NullPointerException
будет брошен.
Отметьте, что порядок указанных интерфейсов прокси является существенным: два запроса на прокси class с той же самой комбинацией интерфейсов, но в различном порядке приведут к двум отличным прокси-классам. Прокси-классы отличают по приказу их интерфейсов прокси, чтобы обеспечить детерминированное кодирование вызова метода в случаях, где два или больше из интерфейсов прокси совместно используют метод с тем же самым именем и подписью параметра; это рассуждение описывается более подробно в разделе ниже названных Методов, Дублированных в Многократных Интерфейсах Прокси.
Так, чтобы новый прокси class не должен был быть сгенерирован каждый раз Proxy.getProxyClass
вызывается с тем же самым загрузчиком class и списком интерфейсов, реализацией динамического прокси, который API class должен сохранить кэшем сгенерированных прокси-классов, включенных их соответствующими загрузчиками и списком интерфейса. Реализация должна бояться обращаться к загрузчикам class, интерфейсам, и прокси-классам таким способом как, чтобы предотвратить загрузчики class, и все их классы, от того, чтобы быть собранным "мусор" когда приспособлено.
У прокси class есть следующие свойства:
"$Proxy"
должен, однако, быть зарезервирован для прокси-классов.java.lang.reflect.Proxy
.getInterfaces
на Class
объект возвратит массив, содержащий тот же самый список интерфейсов (в порядке, определенном при его создании), вызывая getMethods
на Class
объект возвратит массив Method
объекты, которые включают все методы в тех интерфейсах, и вызов getMethod
найдет методы в интерфейсах прокси, как ожидался бы.Proxy.isProxyClass
метод возвратит true, если это передадут прокси class - class, возвращенный Proxy.getProxyClass
или class объекта, возвращенного Proxy.newProxyInstance
- и ложь иначе. Надежность этого метода важна для возможности использовать это, чтобы принять решения безопасности, таким образом, ее реализация не должна просто протестировать, если рассматриваемый class расширяется java.lang.reflect.Proxy
.java.security.ProtectionDomain
из прокси class является тем же самым как тем из системных классов, загруженных начальной загрузкой загрузчик class, такой как java.lang.Object
, потому что код для прокси class сгенерирован кодом достоверной системы. Этот домен защиты будут обычно предоставлять java.security.AllPermission
.У каждого class прокси есть один общедоступный конструктор, который берет один параметр, реализацию интерфейса InvocationHandler
.
У каждого экземпляра прокси есть связанный объект-обработчик вызова, тот, который передали его конструктору. Вместо того, чтобы иметь необходимость использовать API Reflection, чтобы получить доступ к общедоступному конструктору, экземпляр прокси может быть также быть созданным, вызывая Proxy.newProxyInstance
метод, который комбинирует действия вызова Proxy.getProxyClass
с вызовом конструктора с обработчиком вызова. Proxy.newProxyInstance
броски IllegalArgumentException
по тем же самым причинам это Proxy.getProxyClass
делает.
У экземпляра прокси есть следующие свойства:
proxy
и один из интерфейсов, реализованных его прокси class Foo
, следующее выражение возвратит true:
proxy instanceof Foo
и следующая работа броска успешно выполнится (вместо того, чтобы бросить a ClassCastException
):
(Foo) proxy
Proxy.getInvocationHandler
метод возвратит обработчик вызова, связанный с экземпляром прокси, который передают как его параметр. Если объект, к которому передают Proxy.getInvocationHandler
не экземпляр прокси, затем IllegalArgumentException
будет брошен.invoke
метод как описано ниже. Экземпляр самого прокси передадут как первый параметр invoke
, который имеет тип Object
.
Второй параметр, к которому передают invoke
будет java.lang.reflect.Method
экземпляр, соответствующий методу интерфейса, вызывается на экземпляр прокси. Объявление class Method
объект будет интерфейсом, в котором был объявлен метод, который может быть суперинтерфейсом интерфейса прокси, что прокси class наследовал метод через.
Третий параметр, к которому передают invoke
будет массив объектов, содержащих значения параметров, которые передают в вызове метода экземпляру прокси. Параметры типов примитивов обертываются в экземпляр соответствующей примитивной обертки class, такой как java.lang.Integer
или java.lang.Boolean
. Реализация invoke
метод свободен изменить содержание этого массива.
Значение, возвращенное invoke
метод станет возвращаемым значением вызова метода на экземпляре прокси. Если объявленное возвращаемое значение метода интерфейса является типом примитива, то значение, возвращенное invoke
должен быть экземпляр соответствующей примитивной обертки class; иначе, это должен быть тип, присваиваемый объявленному типу возврата. Если значение, возвращенное invoke
null
и тип возврата метода интерфейса примитивен, тогда a NullPointerException
будет брошен вызовом метода на экземпляре прокси. Если значение, возвращенное invoke
является иначе не совместимым с объявленным типом возврата метода как описано выше, a ClassCastException
будет брошен экземпляром прокси.
Если исключение выдается invoke
метод, это будет также брошено вызовом метода на экземпляре прокси. Тип исключения должен быть присваиваемым или любому из типов исключения, объявленных в подписи метода интерфейса или к типам исключения непроверенным java.lang.RuntimeException
или java.lang.Error
. Если проверенное исключение выдается invoke
это не присваиваемо любому из типов исключения, объявленных в throws
пункт метода интерфейса, затем UndeclaredThrowableException
будет брошен вызовом метода на экземпляре прокси. UndeclaredThrowableException
будет создан за исключением того, что был брошен invoke
метод.
hashCode
, equals
, или toString
методы, объявленные в java.lang.Object
на прокси экземпляр будет закодирован и диспетчеризирован обработчику вызова invoke
метод тем же самым способом как вызовы метода интерфейса кодируется и диспетчеризируется, как описано выше. Объявление class Method
объект, к которому передают invoke
будет java.lang.Object
. Другие открытые методы экземпляра прокси, наследованного от java.lang.Object
не переопределяются прокси class, таким образом, вызовы тех методов ведут себя как, они делают для экземпляров java.lang.Object
.Когда два или больше интерфейса прокси, class содержит метод с тем же самым именем и подписью параметра, порядком интерфейсов class прокси, становятся существенными. Когда такой двойной метод вызывается на экземпляр прокси, Method
объект, который передают к обработчику вызова, не обязательно будет тем, объявление которого class присваиваемо от ссылочного типа интерфейса, что метод прокси был вызван через. Это ограничение существует, потому что соответствующая реализация метода в сгенерированном прокси, который не может определить class, какой интерфейс это было вызвано через. Поэтому, когда двойной метод вызывается на экземпляр прокси, Method
объект для метода в передовом интерфейсе, который содержит метод (или непосредственно или наследованный через суперинтерфейс) в списке class прокси интерфейсов, передают к обработчику вызова invoke
метод, независимо от ссылочного типа, через который произошел вызов метода.
Если интерфейс прокси содержит метод с тем же самым именем и подписью параметра как hashCode
, equals
, или toString
методы java.lang.Object
, когда такой метод вызывается на экземпляр прокси, Method
объект, который передают к обработчику вызова, будет иметь java.lang.Object
как его объявление class. Другими словами, общественность, незаключительные методы java.lang.Object
логически предшествуйте всем интерфейсам прокси для определения который Method
возразите, чтобы передать к обработчику вызова.
Отметьте также это, когда двойной метод диспетчеризируется обработчику вызова, invoke
метод может только бросить проверенные типы исключения, которые присваиваемы одному из исключения, вводит throws
пункт метода во всех интерфейсах прокси, что это может быть вызвано через. Если invoke
метод выдает проверенное исключение, которое не присваиваемо любому из типов исключения, объявленных методом в одном из интерфейсов прокси, что это может быть вызвано через, затем непроверенное UndeclaredThrowableException
будет брошен вызовом на экземпляре прокси. Это ограничение означает что не все типы исключения, возвращенные, вызывая getExceptionTypes
на Method
объект, который передают к invoke
метод может обязательно быть брошен успешно invoke
метод.
С тех пор java.lang.reflect.Proxy
реализации java.io.Serializable
, экземпляры прокси могут быть сериализированы, как описано в этом разделе. Если экземпляр прокси содержит обработчик вызова, который не присваиваем java.io.Serializable
, однако, тогда a java.io.NotSerializableException
будет брошен, если такой экземпляр будет записан a java.io.ObjectOutputStream
. Отметьте это прокси-классами, реализовывая java.io.Externalizable
имеет тот же самый эффект относительно сериализации как реализация java.io.Serializable
: writeExternal
и readExternal
методы Externalizable
интерфейс никогда не будет вызываться на экземпляр прокси (или обработчик вызова) как часть его процесса сериализации. Как со всеми Class
объекты, Class
объект для прокси class всегда сериализуем.
У прокси class нет никаких сериализуемых полей и a serialVersionUID
из 0L
. Другими словами, когда Class
объект для прокси class передают к помехам lookup
метод java.io.ObjectStreamClass
, возвращенный ObjectStreamClass
у экземпляра будут следующие свойства:
getSerialVersionUID
метод возвратится 0L
.getFields
метод возвратит массив нуля длины.getField
метод с любым String
параметр возвратится null
.Потоковый протокол для Объектной Сериализации поддерживает названный код типа TC_PROXYCLASSDESC
, который является терминальным символом в грамматике для потокового формата; его тип и значение определяются следующим постоянным полем в java.io.ObjectStreamConstants
интерфейс:
final static byte TC_PROXYCLASSDESC = (byte)0x7D;
Грамматика также включает следующие два правила, первое, являющееся альтернативным расширением исходного правила newClassDesc:
newClassDesc:
TC_PROXYCLASSDESC
newHandle proxyClassDescInfo
proxyClassDescInfo:
(int)<count>
proxyInterfaceName [количество] classAnnotation
superClassDesc
proxyInterfaceName:
(utf)
Когда ObjectOutputStream
сериализирует дескриптор class для class, который является прокси class, как определено, передавая Class
возразите против Proxy.isProxyClass
метод, это использует TC_PROXYCLASSDESC
введите код вместо TC_CLASSDESC
, после правил выше. В расширении proxyClassDescInfo последовательность proxyInterfaceName элементов является именами всех интерфейсов, реализованных прокси class в порядке, что они возвращаются, вызывая getInterfaces
метод на Class
объект. У classAnnotation и superClassDesc элементов есть то же самое значение, как они делают в правиле classDescInfo. Для прокси class, superClassDesc является дескриптором class для своего суперкласса, java.lang.reflect.Proxy
; включая этот дескриптор учитывает развитие сериализированного представления class Proxy
для экземпляров прокси.
Для непрокси-классов, ObjectOutputStream
вызывает его защищенный annotateClass
метод, чтобы позволить подклассам писать пользовательские данные в поток для определенного class. Для прокси-классов, вместо annotateClass
, следующий метод в java.io.ObjectOutputStream
вызывается с Class
объект для прокси class:
protected void annotateProxyClass(Class cl) throws IOException;
Реализация по умолчанию annotateProxyClass
в ObjectOutputStream
ничего не делает.
Когда ObjectInputStream
встречается с кодом типа TC_PROXYCLASSDESC
, это десериализовывает дескриптор class для прокси class от потока, отформатированного как описано выше. Вместо того, чтобы вызвать resolveClass
метод, чтобы решить Class
объект для дескриптора class, следующего метода в java.io.ObjectInputStream
вызывается:
protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException;
Список имен интерфейса, которые были десериализованы в прокси дескриптор class, передают как interfaces
параметр resolveProxyClass
.
Реализация по умолчанию resolveProxyClass
в ObjectInputStream
возвращает результаты вызова Proxy.getProxyClass
со списком Class
объекты для интерфейсов, названных в interfaces
параметр. Class
объект используется для каждого имени интерфейса i
значение, повторно настроенное, вызывая
Class.forName(i, false, loader)где
loader
первый ненулевой загрузчик class стек выполнения, или null
если никакие ненулевые загрузчики class не находятся на стеке. Это - тот же самый выбор загрузчика class, сделанный поведением значения по умолчанию resolveClass
метод. Это то же самое значение loader
также загрузчик class, к которому передают Proxy.getProxyClass
. Если Proxy.getProxyClass
броски IllegalArgumentException
, resolveClass
бросит a ClassNotFoundException
содержа IllegalArgumentException
. Начиная с прокси у class никогда нет своих собственных сериализуемых полей, classdata [] в потоковом представлении экземпляра прокси состоит полностью из данных экземпляра для его суперкласса, java.lang.reflect.Proxy
. Proxy
имеет одно сериализуемое поле, h
, который содержит обработчик вызова для экземпляра прокси.
Вот простой пример, который распечатывает сообщение прежде и после вызова метода на объекте, который реализует произвольный список интерфейсов:
public interface Foo { Object bar(Object obj) throws BazException; } public class FooImpl implements Foo { Object bar(Object obj) throws BazException { // ... } } public class DebugProxy implements java.lang.reflect.InvocationHandler { private Object obj; public static Object newInstance(Object obj) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new DebugProxy(obj)); } private DebugProxy(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Object result; try { System.out.println("before method " + m.getName()); result = m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("after method " + m.getName()); } return result; } }
Создать a DebugProxy
для реализации Foo
соедините интерфейсом и вызовите один из его методов:
Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); foo.bar(null);
Вот является пример служебного обработчика вызова class, который обеспечивает поведение прокси значения по умолчанию для методов, наследованных от java.lang.Object
и делегация реализаций определенных вызовов метода прокси к отличным объектам в зависимости от интерфейса вызванного метода:
import java.lang.reflect.*; public class Delegator implements InvocationHandler { // preloaded Method objects for the methods in java.lang.Object private static Method hashCodeMethod; private static Method equalsMethod; private static Method toStringMethod; static { try { hashCodeMethod = Object.class.getMethod("hashCode", null); equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class }); toStringMethod = Object.class.getMethod("toString", null); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } private Class[] interfaces; private Object[] delegates; public Delegator(Class[] interfaces, Object[] delegates) { this.interfaces = (Class[]) interfaces.clone(); this.delegates = (Object[]) delegates.clone(); } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { Class declaringClass = m.getDeclaringClass(); if (declaringClass == Object.class) { if (m.equals(hashCodeMethod)) { return proxyHashCode(proxy); } else if (m.equals(equalsMethod)) { return proxyEquals(proxy, args[0]); } else if (m.equals(toStringMethod)) { return proxyToString(proxy); } else { throw new InternalError( "unexpected Object method dispatched: " + m); } } else { for (int i = 0; i < interfaces.length; i++) { if (declaringClass.isAssignableFrom(interfaces[i])) { try { return m.invoke(delegates[i], args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } return invokeNotDelegated(proxy, m, args); } } protected Object invokeNotDelegated(Object proxy, Method m, Object[] args) throws Throwable { throw new InternalError("unexpected method dispatched: " + m); } protected Integer proxyHashCode(Object proxy) { return new Integer(System.identityHashCode(proxy)); } protected Boolean proxyEquals(Object proxy, Object other) { return (proxy == other ? Boolean.TRUE : Boolean.FALSE); } protected String proxyToString(Object proxy) { return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); } }
Подклассы Delegator
может переопределить invokeNotDelegated
чтобы реализовать поведение вызовов метода прокси, которые не будут непосредственно делегированы к другим объектам, и они могут переопределить proxyHashCode
, proxyEquals
, и proxyToString
чтобы переопределить поведение значения по умолчанию методов, прокси наследовался от java.lang.Object
.
Создать a Delegator
для реализации Foo
интерфейс:
Class[] proxyInterfaces = new Class[] { Foo.class }; Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces, new Delegator(proxyInterfaces, new Object[] { new FooImpl() }));
Отметьте что реализация Delegator
class, данный выше, предназначается, чтобы быть более иллюстративным чем оптимизированный; например, вместо того, чтобы кэшироваться и сравниться Method
объекты для hashCode
, equals
, и toString
методы, это могло только соответствовать им их названиями строк, потому что ни одни из тех имен методов не перегружаются в java.lang.Object
.