2

这是我遇到的问题。有一个巨大的遗留应用程序在 java 1.3 上运行并使用外部 API,例如 MyAPI v1.0。MyAPI 1.0 的确切实现位于应用程序使用的类路径中的某个位置。还有一种机制允许该应用程序使用外部代码(某种插件机制)。现在我有另一个使用 MyAPI v2.0(与 v1.0 不是 100% 向后兼容)的 java 库 (MyLib.jar),我必须使用该插件机制从原始应用程序中使用它。所以我必须以某种方式让同一个 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

4 回答 4

3

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

说了这么多,为什么你的整个 MyLib.jar 不会被一个自定义的类加载器加载,然后它可以以常规方式引用更新版本的 API。否则你会遇到问题,因为你将不得不一直使用 Object 类型和反射。

于 2009-02-05T12:48:56.420 回答
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 的方法。这样,您可以进行一次反射调用来启动应用程序,并且该应用程序内的所有内容都只使用标准引用。

于 2009-02-05T12:41:32.363 回答
0

你可以用一个花哨的 ClassLoader 来做到这一点,而无需反射。

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

它比这更复杂一些,但如果你从这个想法开始,你就会得到解决。

于 2009-02-05T12:43:07.353 回答
0

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

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

在父类加载器上调用 loadClass 方法。如果 parent 为 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 , boolean)

于 2009-02-05T12:43:47.367 回答