显然你想在运行时重新加载类。这样您的项目就可以对代码的更改做出反应而无需重新启动。
要实现这一点,您需要准备项目并编写一个非常干净的架构,其中涉及使用 interfaces、factory-patterns、proxy-patterns strong> 和一个检查更新然后销毁并重建所有当前对象的例程。
不幸的是,这可能不是一件容易的事,但它是可行的,具体取决于您的项目的大小和应动态响应更改的代码量。
我发现this article 很有帮助,让我解释一下它是如何工作的。您可以使用ClassLoader.loadClass(...) 轻松加载类,也可以使用它重新加载类,非常简单。但是,在您编译代码时,您的代码类已经是某种硬连线了。因此,您的旧代码将继续创建旧类的实例,尽管您已经重新加载了类。
这就是为什么我们需要某种允许旧类与新类交换的架构的原因。同样很明显,旧类的当前实例不能自动转移到新版本,因为一切都可能发生变化。因此,您还需要一个自定义方法来收集和重建这些实例。
文章中描述的方法首先使用Interface 而不是实际的类。这允许在不破坏使用该接口的代码的情况下轻松地交换该接口后面的类。
然后您需要一个工厂,您可以在其中请求 Interface 的实例。工厂现在可以检查底层类文件是否已更改,如果是,则重新加载它并获取对新类版本的引用。它现在总是可以创建一个使用最新类的接口实例。
工厂还可以收集所有创建的实例,以便以后在代码库发生更改时进行交换。但是工厂应该使用WeakReference (documentation) 来引用它们,否则你会有很大的内存泄漏,因为垃圾收集器将无法删除实例,因为工厂持有对它们的引用。
好的,现在我们总能获得Interface 的最新实现。但是我们如何才能轻松地交换现有实例。答案是使用代理模式 (explanation)。
很简单,您有一个 代理类,它是您正在使用的实际对象。它具有Interface 的所有方法,并且在调用方法时它只是转发给真实类。
您的工厂,因为它有一个使用 WeakReference 的所有当前实例的列表,现在可以迭代代理列表并用新的最新版本的对象交换它们的真实类。
在您的项目中使用的现有代理现在将自动使用新的真实版本,因为代理本身没有改变,只是它对真实目标的内部引用发生了变化。
现在一些示例代码可以让您大致了解一下。
您要监控的对象的界面:
public interface IExample {
void example();
}
您要重建的真实类:
public class RealExample implements IExample {
@Override
public void example() {
System.out.println("Hi there.");
}
}
您将实际使用的代理类:
public class ProxyExample implements IExample {
private IExample mTarget;
public ProxyExample(final IExample target) {
this.mTarget = target;
}
@Override
public void example() {
// Forward to the real implementation
this.mRealExample.example();
}
public void exchangeTarget(final IExample target) {
this.mTarget = target;
}
}
您将主要使用的工厂:
public class ExampleFactory {
private static final String CLASS_NAME_TO_MONITOR = "somePackage.RealExample";
private final List<WeakReference<ProxyExample>> mInstances;
private final URLClassLoader mClassLoader;
public ExampleFactory() {
mInstances = new LinkedList<>();
// Classloader that will always load the up-to-date version of the class to monitor
mClassLoader = new URLClassLoader(new URL[] {getClassPath()}) {
public Class loadClass(final String name) {
if (CLASS_NAME_TO_MONITOR.equals(name)) {
return findClass(name);
}
return super.loadClass(name);
}
};
}
private IExample createRealInstance() {
return (IExample) this.mClassLoader.loadClass(CLASS_NAME_TO_MONITOR).newInstance();
}
public IExample createInstance() {
// Create an up-to-date instance
final IExample instance = createRealInstance();
// Create a proxy around it
final ProxyExample proxy = new ProxyExample(instance);
// Add the proxy to the monitor
this.mInstances.add(proxy);
return proxy;
}
public void updateAllInstances() {
// Iterate the proxies and update their references
// Use a ListIterator to easily remove instances that have been cleared
final ListIterator<WeakReference<ProxyExample>> instanceIter =
this.mInstances.listIterator();
while (instanceIter.hasNext()) {
final WeakReference<ProxyExample> reference = instanceIter.next();
final ProxyExample proxy = reference.get();
// Remove the instance if it was already cleared,
// for example by the garbage collector
if (proxy == null) {
instanceIter.remove();
continue;
}
// Create an up-to-date instance for exchange
final IExample instance = createRealInstance();
// Update the target of the proxy instance
proxy.exchangeTarget(instance);
}
}
}
最后如何使用:
public static void main(final String[] args) {
final ExampleFactory factory = new ExampleFactory();
// Get some instances using the factory
final IExample example1 = factory.createInstance();
final IExample example2 = factory.createInstance();
// Prints "Hi there."
example1.example();
// Update all instances
factory.updateAllInstances();
// Prints whatever the class now contains
example1.example();
}