代码之家  ›  专栏  ›  技术社区  ›  Shuxin

为什么创建Java动态代理需要接口参数

  •  1
  • Shuxin  · 技术社区  · 1 年前

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

    根据我的理解,我们使用classloader来显示代理对象应该被解释为什么样的对象。因此,它将属于我们提供classloader参数的类。在这种情况下,它应该具有与该类中定义的方法完全相同的方法,那么为什么我们仍然必须为它提供第二个参数接口呢?

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

    2 回复  |  直到 1 年前
        1
  •  3
  •   Sweeper    1 年前

    您似乎认为第一个参数是指定代理对象实现的接口的参数。这不是真的——指定代理应该实现的接口是第二个参数的工作。您可以提供多个接口,作为 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 是由系统类加载器加载的,因为这是我编写的一个接口。

        2
  •  0
  •   Shuxin    1 年前

    我想我误解了类加载器和类之间的关系。非常感谢大家,尤其是 Mark Rotteveel 因为他的回答很清楚地解决了我的问题。我在下面引用他的回答来结束这个问题:

    在最简单的情况下,类加载器加载应用程序中的所有类。在任何情况下,代理的类加载器都不需要是加载原始类或接口的加载器,它只需要是用于定义代理的加载器。