ChatGPT解决这个技术问题 Extra ChatGPT

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

根据the documentation

[java.lang.reflect.]Proxy 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

newProxyMethod method(负责生成动态代理)具有以下签名:

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

C
Cristian Ciupitu

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

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

产生这个输出:

Woof!
Handling public abstract void mock.Dog.fetch() via the method handler

+1:正是我需要的!我将使用我的示例代码编辑您的答案。
proxyFactory.setHandler() 已弃用。请使用 proxy.setHandler
@axtavt 对象“Dog”是上述代码中的实现还是接口?
c
chatellier

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

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

如果我以您的 dog 类为例,您的调用处理程序的调用方法可能如下所示(现有关联的 dog 子类称为 ..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 源代码的严格检查所揭示的那样,您可能会发现(在 Proxy.java:362 处)不可能为不代表接口的 Class 对象创建代理)。

所以,抛开现实,你想做的事是完全可能的。


当我试图理解你的答案时,请耐心等待......在我的特殊情况下,我希望 proxy 类(在运行时生成)成为 Dog 的子类(例如,我m 未明确编写实现 fetch()Poodle 类)。因此,没有 dog 变量可以调用这些方法......对不起,如果我感到困惑,我将不得不再考虑一下。
@Adam - 如果没有一些字节码操作,你不能在运行时动态创建子类(我认为 CGLib 会做这样的事情)。简短的回答是动态代理支持接口,但不支持抽象类,因为两者是非常不同的概念。几乎不可能想出一种以理智的方式动态代理抽象类的方法。
@Andrzej:我知道我所要求的需要字节码操作(事实上,我已经使用 ASM 编写了我的问题的解决方案)。我也明白 Java 的动态代理只支持接口。也许我的问题并不完全清楚 - 我在问是否有 任何其他 类(即 java.lang.reflect.Proxy 以外的东西)可以满足我的需要。
好吧,长话短说……不(至少在标准 Java 类中)。使用字节码操作,天空就是极限!
我投了反对票,因为这并不是问题的真正答案。 OP 表示他想代理一个类,而不是一个接口,并且知道使用 java.lang.reflect.Proxy 是不可能的。你只是重复这个事实,没有提供其他解决方案。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅