使用Java Proxy创建新的动态代理实例的方法具有如下签名.

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

我想知道为什么需要第二个参数接口? 如下例所示,这就是教程通常显示使用模式的方式:

Hello hello = new Hello();
IHello proxy = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),
            new Class[] {IHello.class},
            new HelloHandler(hello));
proxy.hello();

根据我的理解,我们使用类加载器来显示应该将代理对象解释为哪种对象.因此,它将属于我们为其提供了类加载器参数的类.在这种情况下,它应该具有与该类中定义的方法完全相同的方法,那么为什么我们还要为它提供第二个参数接口呢?

编辑: 我真正的好奇心在于,我们什么时候会为类加载器和接口使用不同的类?(A.class.getClassLoader()和new Class[]{B.class})从注释和实验代码中我知道这是可能的.然而,这些效果似乎没有任何变化(或使程序失败).那么,会不会出现应该使用不同类的情况呢?(不仅仅是可以).

推荐答案

您似乎认为第一个参数指定代理对象实现的接口.这不是真的-指定代理应该实现的接口是第二个参数的工作.您可以给出多个接口,比如Class[],来说明代理应该实现.

第一个参数指定应该使用哪个类加载器来定义代理对象的类.

如果您不理解什么是类加载器,请看What is a Java ClassLoader?.

在您的示例中,IHello.class.getClassLoader()作为第一个参数传递.IHello.class出现在该表达式中这一事实与我们希望代理实现什么接口无关.IHello.class.getClassLoader()只获取加载IHello的类加载器.我也可以传递ClassLoader.getSystemClassLoader()或我自己的ClassLoader实现作为第一个参数.

重要的是,第一个参数是一个类加载器,它满足documentation中列出的一些要求.与这个问题最相关的问题是:

  • 所有接口类型必须通过指定的类加载器按名称可见.
  • 由指定接口的所有公共方法签名引用的所有类型以及由其超接口继承的类型必须 通过指定的类加载器按名称可见.

如果不满足任何要求,newProxyInstance将引发异常.

您经常会看到人们使用SomeInterface.class.getClassLoader(),然后传递SomeInterface.class作为第二个参数,因为通过加载SomeInterface的类加载器,SomeInterface很容易"按名称可见",因此它绝对满足要求.

作为另一个示例,下面是对newProxyInstance的调用,它将引发异常:

Proxy.newProxyInstance(String.class.getClassLoader(),
        new Class[] { I.class },
        (a, b, c) -> {
            System.out.println("Foo");
            return true;
        });

// ...

interface I {}

在这里,String.class由平台类加载器加载(因为它是一个JDK类),但I由系统类加载器加载,因为它是我编写的接口.

Java相关问答推荐

为什么如果数组列表中有重复项,我的代码SOMETIMES不返回true?

最小拓Flutter 排序的时间复杂度是多少?

S的字符串表示是双重精确的吗?

连接Quarkus中的两个异步操作

为什么JAVA&S清洁器使用链表而不是并发HashSet?

在springboot 3中,当我调用api endpoint时,会出现404

我不能再在Android Studio Hedgehog上用Java语言创建新项目了吗?

无法了解Java线程所消耗的时间

为什么S的文档中说常量方法句柄不能在类的常量池中表示?

无法在Java中处理PayPal支付响应

SonarLint:只能有条件地调用方法(S)

Spring-Boot Kafka应用程序到GraalVM本机映像-找不到org.apache.kafka.streams.processor.internals.DefaultKafkaClientSupplier

如果按钮符合某些期望,如何修改它的文本?

如何用内置Java从JavaFX应用程序中生成.exe文件?

如何创建模块信息类文件并将其添加到JAR中?

如何在Java springboot中从一个端点发送多个时间响应?

在权限列表中找不到我的应用程序

为什么Spring要更改Java版本配置以及如何正确设置?

AspectJ编织外部依赖代码,重新打包jar并强制依赖用户使用它

Java方法参数:括号中的类型声明?