可以动态加载类重新绑定到静态引用吗?

时间:2016-08-27 10:45:46

标签: java

Reference: dynamic load java code

public interface Postman {
    void deliverMessage(String msg);
}

//loaded by dynamic class loader, with parent class loader pointing to main
public class PostmanImpl implements Postman {
}

public class PostmanApp {
    public static void main(String[] args) throws Exception {
        Postman postman = getPostman();
        while (true) {
            //postman.deliverMessage(msg);
            getPostman().deliverMessage(msg);
        }
    }
    private static Postman getPostman() {
        // Omit for now, will come back later
        // ???
    }
 }
  

如何使用静态引用访问最新的动态类?显然,对动态类对象的直接(正常)引用不会起作用。

我不太遵循上述说法。不是apparent给我的。动态类加载器将始终引用main类加载器作为父类加载器,因此Postman接口始终有效,动态类加载器始终加载一个新的Impl类,该类与interface

InvocationHandler handler = new DynaCodeInvocationHandler(...);
    Postman proxy = (Postman) Proxy.newProxyInstance(
                Postman.class.getClassLoader(),
                new Class[] { Postman.class },
                handler);

作者最终建议使用Proxy将接口上的调用委托给新加载的实现。

  

显然,对动态类对象的直接(正常)引用不会起作用。

新加载的类的直接引用赋值有什么问题?为什么最终需要Proxy

3 个答案:

答案 0 :(得分:2)

Java不允许在运行时更改实例类型。即使结构上等效或甚至相同的字节码(它们可能共享一个共同的超类或接口),动态加载的类总是一个与旧类不兼容的新类。

因此,如果您将普通实例分发给客户,则无法“回忆”它们。您只能使实例失效并强制客户端获取新实例。

代理允许您更改将调用重定向到的代理。所以你将代理分发给你的客户。加载类的新版本时,您可以更改代理的属性。所有客户都可以继续处理他们的代理参考。

答案 1 :(得分:1)

使类动态化的关键是我们能够根据需要将引用重定向到动态类。

如果没有代理我们就无法做到:

No proxy

以红色显示的链接表示从Java变量到对象的引用。我们不拥有变量,我们不知道它是如何使用的。客户端可以将变量作为参数传递,或将其分配给字段,从而制作红色链接的多个副本,从而无法对其进行管理。

代理添加了另一个间接级别:

With a proxy

我们仍然没有红色链接。但是,绿色链接在代理内部,因此它完全在我们的控制之下。如果我们决定将其重定向到另一个对象,并且我们有多个客户端引用它,则所有客户端对象将在重新分配后重定向。

答案 2 :(得分:1)

您已经修复了代码,因此没有必要。

原始代码执行此操作(简化):

Postman p = getPostman();
while(true) {
    p.deliverMessage();
}

Postman中的代码无法更改p引用的对象。当它加载更新的类并创建新类的新实例时,p仍然引用旧类的旧实例。

但如果你这样做:

while(true) {
    getPostman().deliverMessage();
}

然后getPostman将在加载后返回新类的实例,并且没有问题。