我自己的类加载器?

时间:2009-02-05 12:29:06

标签: java classloader

这是我遇到的问题。有一个巨大的遗留应用程序在java 1.3上运行并使用外部API,比如MyAPI v1.0。 MyAPI 1.0的确切实现位于应用程序使用的类路径中的某个位置。还有一种机制允许该应用程序使用外部代码(某种插件机制)。现在我有另一个使用MyAPI v2.0的Java库(MyLib.jar)(它与v1.0不是100%向后兼容),我必须使用该插件机制从原始应用程序中使用它。所以我必须以某种方式让相同API的两个(不兼容!)版本一起工作。具体来说,我想在从MyLib.jar类调用API类时使用MyAPI v2.0,并在所有其他情况下使用MyAPI 1.0。

MyAPI 1.0在类路径中,因此默认情况下会使用它,没关系。我可以创建自己的类加载器版本来从MyAPI 2.0加载类 - 没问题。但是我如何将它们整合在一起呢?问题:

  1. MyLib.jar对象从MyAPI 2.0实例化了很多(!)类的实例。这是否意味着我必须通过反射(指定我自己的类加载器)来完成所有这些实例化?这是一件很棒的工作!

  2. 如果某些MyAPI 2.0对象被实例化并且它在内部实例化MyAPI中的另一个对象,那么它将使用哪个类加载器?它会使用我的类加载器还是默认加载器?

  3. 一般来说,我的方法听起来合理吗?有更好的方法吗?

4 个答案:

答案 0 :(得分:3)

让我们从回答你的第二个问题开始:当从一个类引用另一个类时,它将被加载原始类的同一个类加载器加载(除非类加载器没有成功找到类,然后它将会委托给它的父类加载器。)

话虽如此,为什么整个MyLib.jar都不会被自定义类加载器加载,然后它可以定期引用更新版本的API。否则你会遇到问题,因为你必须一直使用Object类型和反射。

答案 1 :(得分:2)

你需要小心类加载器。如果你按照你的建议行事,那么即使在为MyAPI 2.0使用类加载器时,你也几乎总是会结束MyAPI 1.0。这样做的原因是如何使用类加载器加载类。始终首先从父类加载器加载类。

“ClassLoader类使用委托模型来搜索类和资源.ClassLoader的每个实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader实例将委派对类或资源的搜索在尝试查找类或资源本身之前,它的父类加载器。虚拟机的内置类加载器,称为“引导类加载器”,本身不具有父级,但可以作为ClassLoader实例的父级。 (http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html

要正确提供两个API之间的隔离,您需要2个类加载器(或者除主应用程序之外还需要2个)。

Parent - System classloader
  |- Classloader1 - Used to load MyAPI 1.0
  |- Classloader2 - Used to load MyAPI 2.0

现在回答你的问题。您可能想要做的是将使用API​​的大多数逻辑移动到类加载器中。除了MyAPI 1.0 / 2.0之外,您还应该加载使用它们的应用程序部分。然后,父应用程序只需调用使用API​​的方法。这样,您可以进行单个反射调用以启动应用程序,并且该应用程序内的所有内容都只使用标准引用。

答案 2 :(得分:0)

你可以用一个没有反射的花哨的ClassLoader来做到这一点。

基本上,类加载器必须说“如果加载类来自此jar,则从类路径B加载,否则使用主类ClassLoader”。

这比这复杂一点,但如果你从这个想法开始,你就会明白它。

答案 3 :(得分:0)

这听起来很合理。在[API javadoc for loadClass] [1]中,它说:

“使用指定的二进制名称加载类。此方法的默认实现按以下顺序搜索类: 调用findLoadedClass(String)来检查该类是否已被加载。

在父类加载器上调用loadClass方法。如果父项为null,则使用内置于虚拟机的类加载器。

调用findClass(String)方法来查找类。“

如果CL1用于MyAPI1,CL2用于MyAPI2,而CL3用于MyLib,则听起来您希望按顺序检查它们:CL3,CL2,CL1。从上面的引用(首先检查父项)建议你希望CL1有父CL2,CL2有父CL3。由于具有父类加载器的构造函数受到保护,因此您必须使用URL类加载器并将父类设置为适当的。

这样的东西
URLCLassLoader cl3 = new URLClassLoader(new URL[]{ path to MyLib});
URLCLassLoader cl2 = new URLClassLoader(new URL[]{ path to API2}, cl3);
URLCLassLoader cl1 = new URLClassLoader(new URL[]{ path to API1}, cl2);

然后到处使用cl1。

[1]:http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,布尔值)