Альтернативы java.lang.reflect. Прокси для создания прокси абстрактных классов (а не интерфейсы)

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

[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 это может генерировать динамические прокси, которые наследовались определенному абстрактному классу, перенаправляя все вызовы к абстрактным методам для обработчика вызовов?

Например, предположите, что у меня есть абстрактный класс 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

80
задан Adam Paynter 21 July 2010 в 11:07
поделиться

2 ответа

Это можно сделать с помощью 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
110
ответ дан 24 November 2019 в 10:00
поделиться

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

Вам, конечно, придется его закодировать, однако это довольно просто. Для создания вашего прокси вам нужно будет передать ему InvocationHandler. Затем вам нужно будет только проверить тип метода в методе invoke(...) вашего обработчика вызовов. Но учтите: вам придется проверять тип метода по базовому объекту, связанному с вашим обработчиком, а не по объявленному типу вашего абстрактного класса.

Если я возьму в качестве примера ваш класс собаки, то метод invoke вашего обработчика вызова может выглядеть следующим образом (с существующим ассоциированным подклассом собаки под названием ... ну ... 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, который не представляет интерфейс).

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

-7
ответ дан 24 November 2019 в 10:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: