Альтернативы java.lang.reflect.Proxy для создания прокси абстрактных классов (а не интерфейсов) - PullRequest
77 голосов
/ 20 июля 2010

Согласно документации :

[java.lang.reflect.] Proxy предоставляет статические методы для создания динамических прокси-классов и экземпляров, а также является суперклассом для всехДинамические прокси-классы, созданные этими методами.

newProxyMethod метод (ответственный за генерацию динамических прокси) имеет следующую подпись:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                             throws IllegalArgumentException

К сожалению, это не позволяет генерировать динамический прокси, который расширяет определенный абстрактный класс (а не , реализующий определенные интерфейсы).Это имеет смысл, учитывая, что java.lang.reflect.Proxy является «суперклассом всех динамических прокси», что препятствует тому, чтобы другой класс был суперклассом.

Поэтому существуют ли альтернативы java.lang.reflect.Proxy, которые могут генерировать динамические прокси, которые наследовать от определенного абстрактного класса, перенаправляя все вызовы abstract методов в обработчик вызовов?

Например, предположим, у меня есть абстрактный класс Dog:

public abstract class Dog {

    public void bark() {
        System.out.println("Woof!");
    }

    public abstract void fetch();

}

Есть ли класс, который позволяет мне делать следующее?

Dog dog = SomeOtherProxy.newProxyInstance(classLoader, Dog.class, h);

dog.fetch(); // Will be handled by the invocation handler
dog.bark();  // Will NOT be handled by the invocation handler

Ответы [ 2 ]

107 голосов
/ 20 июля 2010

Это можно сделать с помощью Javassist (см. ProxyFactory) или CGLIB .

Пример Адама с использованием Javassist:

Я (Адам Пейнтер) написал этот код, используя Javassist:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
    new MethodFilter() {
        @Override
        public boolean isHandled(Method method) {
            return Modifier.isAbstract(method.getModifiers());
        }
    }
);

MethodHandler handler = new MethodHandler() {
    @Override
    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        System.out.println("Handling " + thisMethod + " via the method handler");
        return null;
    }
};

Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();

, который производит этот вывод:

Woof!
Handling public abstract void mock.Dog.fetch() via the method handler
0 голосов
/ 20 июля 2010

В таком случае вы можете использовать прокси-обработчик, который будет перенаправлять вызовы на существующие методы вашего абстрактного класса.

Вам, конечно, придется его кодировать, но все довольно просто. Для создания вашего Прокси вам нужно дать ему <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/InvocationHandler.html" rel="nofollow noreferrer">InvocationHandler</a>. Затем вам нужно будет только проверить тип метода в <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/InvocationHandler.html#invoke(java.lang.Object,%20java.lang.reflect.Method,%20java.lang.Object[])" rel="nofollow noreferrer">invoke(..)</a> методе вашего обработчика вызовов. Но будьте осторожны: вам придется проверять тип метода по базовому объекту, связанному с вашим обработчиком, а не по объявленному типу вашего абстрактного класса.

Если я возьму в качестве примера ваш класс собаки, метод вызова вашего обработчика вызова может выглядеть следующим образом (с существующим связанным подклассом собаки, называемым .. ну ... dog)

public void invoke(Object proxy, Method method, Object[] args) {
    if(!Modifier.isAbstract(method.getModifiers())) {
        method.invoke(dog, args); // with the correct exception handling
    } else {
        // what can we do with abstract methods ?
    }
}

Однако есть кое-что, что заставляет меня задуматься: я говорил об объекте dog. Но поскольку класс Dog является абстрактным, вы не можете создавать экземпляры, поэтому у вас есть существующие подклассы. Кроме того, как показывает тщательная проверка исходного кода Proxy, вы можете обнаружить (в Proxy.java:362), что невозможно создать Proxy для объекта Class, который не представляет интерфейс).

Итак, кроме реальности , то, что вы хотите сделать, вполне возможно.

...