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
?
答案 0 :(得分:2)
Java不允许在运行时更改实例类型。即使结构上等效或甚至相同的字节码(它们可能共享一个共同的超类或接口),动态加载的类总是一个与旧类不兼容的新类。
因此,如果您将普通实例分发给客户,则无法“回忆”它们。您只能使实例失效并强制客户端获取新实例。
代理允许您更改将调用重定向到的代理。所以你将代理分发给你的客户。加载类的新版本时,您可以更改代理的属性。所有客户都可以继续处理他们的代理参考。
答案 1 :(得分:1)
使类动态化的关键是我们能够根据需要将引用重定向到动态类。
如果没有代理我们就无法做到:
以红色显示的链接表示从Java变量到对象的引用。我们不拥有变量,我们不知道它是如何使用的。客户端可以将变量作为参数传递,或将其分配给字段,从而制作红色链接的多个副本,从而无法对其进行管理。
代理添加了另一个间接级别:
我们仍然没有红色链接。但是,绿色链接在代理内部,因此它完全在我们的控制之下。如果我们决定将其重定向到另一个对象,并且我们有多个客户端引用它,则所有客户端对象将在重新分配后重定向。
答案 2 :(得分:1)
您已经修复了代码,因此没有必要。
原始代码执行此操作(简化):
Postman p = getPostman();
while(true) {
p.deliverMessage();
}
Postman
中的代码无法更改p
引用的对象。当它加载更新的类并创建新类的新实例时,p
仍然引用旧类的旧实例。
但如果你这样做:
while(true) {
getPostman().deliverMessage();
}
然后getPostman
将在加载后返回新类的实例,并且没有问题。