java.lang.reflect.Proxy的替代品,用于创建抽象类的代理(而不是接口)


89

根据文档

[ 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

Answers:


123

可以使用Javassist(请参见ProxyFactory)或CGLIB来完成。

亚当使用Javassist的示例:

我(Adam Paynter)使用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();

产生以下输出:

纬!
通过方法处理程序处理公共抽象void mock.Dog.fetch()

10
+1:正是我所需要的!我将用示例代码编辑您的答案。
亚当·佩恩特

proxyFactory.setHandler()不推荐使用。请使用proxy.setHandler
AlikElzin-kilaka

@axtavt是对象“ Dog”在上述代码中是实现还是接口?
stackoverflow

-7

在这种情况下,您可以做的是拥有一个代理处理程序,该处理程序会将调用重定向到抽象类的现有方法。

您当然必须对其进行编码,但这非常简单。要创建代理,您必须给他一个InvocationHandler。然后,您只需要在invoke(..)调用处理程序的方法中检查方法类型。但要注意:您必须对照与您的处理程序关联的基础对象而不是抽象类的声明类型来检查方法类型。

以我的狗类为例,您的调用处理程序的invoke方法可能看起来像这样(具有一个名为.. well ...的现有关联狗子类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.java:362上)无法为不代表接口的Class对象创建代理)。

因此,除了现实之外,您完全可以做您想做的事情。


1
在尝试理解您的答案时,请耐心等待。在我的特殊情况下,我希望(在运行时生成的)代理类成为Dog(例如,我没有明确编写Poodle实现的类fetch())的子类。因此,没有任何dog变量可以在...时调用方法。对不起,如果我感到困惑,我将不得不再考虑一下。
亚当·佩恩特

1
@Adam-如果没有某些字节码操作,就无法在运行时动态创建子类(CGLib,我认为这样做)。简短的答案是动态代理支持接口,但不支持抽象类,因为两者是非常不同的概念。几乎不可能想到一种以理智的方式动态代理抽象类的方法。
Andrzej Doyle

1
@Andrzej:我了解我要的内容需要字节码操作(实际上,我已经使用ASM编写了解决问题的解决方案)。我也了解Java的动态代理仅支持接口。也许我的问题是不完全清楚-我问是否有任何其他类(也就是比其他的东西java.lang.reflect.Proxy)可用,做什么,我需要。
亚当·佩恩特

2
好吧,简而言之……不行(至少在标准Java类中如此)。使用字节码操作,才是极限!
Riduidel

9
我拒绝投票是因为这并不是对问题的真正答案。OP表示他想代理一个类,而不是接口,并且知道使用java.lang.reflect.Proxy是不可能的。您只需重复这一事实,就不提供其他解决方案。
jcsahnwaldt恢复莫妮卡2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.