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

Динамические Прокси-классы

Содержание

Введение
Динамический API Прокси
Сериализация
Примеры

Введение

Динамический прокси class является class, который реализует список интерфейсов, определенных во времени выполнения так, что вызов метода через один из интерфейсов на экземпляре class, будет закодирован и диспетчеризирован другому объекту через универсальный интерфейс. Таким образом динамический прокси class может использоваться, чтобы создать безопасный с точки зрения типов объект прокси для списка интерфейсов, не требуя предварительной генерации прокси class, такой как с инструментами времени компиляции. Вызовы метода на экземпляре динамического прокси, class диспетчеризируется единственному методу в обработчике вызова экземпляра, и они кодируются с a java.lang.reflect.Method объект, идентифицирующий метод, который был вызван и массив типа Object содержа параметры.

Динамические прокси-классы полезны для приложения или библиотеки, которая должна обеспечить безопасный с точки зрения типов отражающий, диспетчеризируют вызовов на объектах тот существующий интерфейс API. Например, приложение может использовать динамический прокси class, чтобы создать объект, который реализует многократные интерфейсы слушателя случайного события - интерфейсы, которые расширяются java.util.EventListener- обработать множество событий различных типов универсальным способом, такой как, регистрируя все такие события к файлу.

Динамический API Прокси-класса

Динамическим 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:

Если какое-либо из этих ограничений нарушается, Proxy.getProxyClass бросит IllegalArgumentException. Если interfaces параметр массива или любой из его элементов null, a NullPointerException будет брошен.

Отметьте, что порядок указанных интерфейсов прокси является существенным: два запроса на прокси class с той же самой комбинацией интерфейсов, но в различном порядке приведут к двум отличным прокси-классам. Прокси-классы отличают по приказу их интерфейсов прокси, чтобы обеспечить детерминированное кодирование вызова метода в случаях, где два или больше из интерфейсов прокси совместно используют метод с тем же самым именем и подписью параметра; это рассуждение описывается более подробно в разделе ниже названных Методов, Дублированных в Многократных Интерфейсах Прокси.

Так, чтобы новый прокси class не должен был быть сгенерирован каждый раз Proxy.getProxyClass вызывается с тем же самым загрузчиком class и списком интерфейсов, реализацией динамического прокси, который API class должен сохранить кэшем сгенерированных прокси-классов, включенных их соответствующими загрузчиками и списком интерфейса. Реализация должна бояться обращаться к загрузчикам class, интерфейсам, и прокси-классам таким способом как, чтобы предотвратить загрузчики class, и все их классы, от того, чтобы быть собранным "мусор" когда приспособлено.

Свойства Прокси-класса

У прокси class есть следующие свойства:

Создание Экземпляра Прокси

У каждого class прокси есть один общедоступный конструктор, который берет один параметр, реализацию интерфейса InvocationHandler.

У каждого экземпляра прокси есть связанный объект-обработчик вызова, тот, который передали его конструктору. Вместо того, чтобы иметь необходимость использовать API Reflection, чтобы получить доступ к общедоступному конструктору, экземпляр прокси может быть также быть созданным, вызывая Proxy.newProxyInstance метод, который комбинирует действия вызова Proxy.getProxyClass с вызовом конструктора с обработчиком вызова. Proxy.newProxyInstance броски IllegalArgumentException по тем же самым причинам это Proxy.getProxyClass делает.

Свойства Экземпляра прокси

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

Методы, Дублированные в Многократных Интерфейсах Прокси

Когда два или больше интерфейса прокси, 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 у экземпляра будут следующие свойства:

Потоковый протокол для Объектной Сериализации поддерживает названный код типа 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.


Oracle и/или его филиалы Авторское право © 1993, 2012, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами