将类文件的多个版本加载到JVM中

问题描述:

是否可以将相同类的多个版本加载到JVM中。我在主线程中加载了类“XYZ”的版本,并且我有多个子线程正在执行一些工作。我想将不同版本的类“XYZ”加载到子线程中。将类文件的多个版本加载到JVM中

这是可行的吗?我正在考虑为每个子线程创建一个新的上下文类加载器,并使用此上下文类加载器加载该类的不同版本。现在我正在使用URLClassLoader作为上下文类加载器,但似乎并不奏效。我是否必须创建一个自定义类加载器来完成这项工作?

这是我到目前为止的代码。

final Thread builderThread = new Thread("Child Thread " + buildToken) { 
     @Override 
     public void run() { 
      futureTask.run(); 
     } 
    }; 

    try { 
     URL url = new URL("file:///path to the jar file"); 
     URLClassLoader classLoader = new URLClassLoader(new URL[]{url}); 
     classLoader.loadClass("XYZ"); 
     builderThread.setContextClassLoader(classLoader); 
    } 

我想要有不同版本的类的原因是因为我想为类“XYZ”中的静态字段有不同的值。

+4

我认为这是一个设计问题。如果你有不同的静态字段,只要让它们不是静态的。 – TMichelsen

+0

那么,XYZ是一个在很多地方使用的遗留类,做这种改变需要很多努力。 – ajaymysore

+0

是否可以通过getter方法公开您的静态字段并根据调用者上下文返回不同的值?它会吸引很多重构吗? –

是的,这是可能的,你在URLClassLoader正确的轨道上。您的类加载器的范围将取决于您希望static字段具有的范围。例如,如果每个任务或线程都有自己的这些字段的副本,那么每个任务或线程都必须具有自己的类加载器。

还有一些其他问题需要注意。首先,要在系统类路径中找到要加载多次的类不得在。这是因为类加载器存在于层次结构中,并在尝试自己加载类之前将其委托给其父代。如果系统类加载器找到该类,则它只会加载一次,其他类加载器将使用由系统类加载器加载的类。其次,JVM会将每个类的加载视为一个不同的类,尽管它们都是完全相同的。这意味着动态加载类的实例必须限制在其类加载器的范围内。如果每个任务都有自己的类加载器,则不能在任务之间传递实例。显然你不能将实例发布到应用程序的其余部分,因为应用程序的其余部分使用系统类加载器,该类加载器一定不知道类。 (你可以解决这个问题有点如果动态加载的类有一个超类或超,系统类加载器了解,然后你可以发布实例作为该类型。)

+0

谢谢。第二个问题对我来说不是问题,因为我不打算通过实例。然而,第一个问题是一个真正的问题,并且是我正在考虑编写自定义类加载器的原因。请注意,父类加载器已经从其类路径加载了XYZ类的一个版本。我正在考虑编写一个自定义的类加载器,该类加载器在将它委托给父类加载器之前尝试加载一个类本身。我知道我需要跳过一些陷阱。例如,除了XYZ类以外,所有类都应遵循相同的类加载器层次结构。想法? – ajaymysore

+0

@ajaymysore这可能工作。但是,这是否有一个原因必须在同一个JVM中完成?如果你有一个使用'XYZ'旧定义的辅助应用程序,你可以避免使用类加载器shenanigans。基本上是主应用程序需要的某种服务器。 –

我之所以要有不同版本的类是因为我想为类“XYZ”中的静态字段具有不同的值。

这对我来说真的很像。你真的没有其他的方法来为这个课程注入不同的价值吗?

如何为每个线程传递某种Context实例并将您的类转换为使用Context而不是static字段中的设置。您也可以使用ThreadLocal,以便每个线程都可以从中获取静态字段信息。

加载该类的不同版本将非常困难(如果不是不可能)调试。我听说过使用多个类加载器的人唯一的一次是当你有安全问题时(通常在Web服务器环境中),并且在Web类和外部管理类之间存在分离的故意点。