Parent Last Classloader解决Java类路径地狱?

我有一个项目使用两个版本的bouncyCastle jars bcprov-jdk15和bcprov-jdk16。 jvm加载旧版本,但我写的一个function需要运行更新的版本。 我试图通过使用自定义类加载器来解决这个类路径地狱。 经过一些谷歌搜索并借助之前的Stackoverflow回答[1] [2]和本博客 ,我编写了以下Parent Last Class加载器,以便在委托给父类加载器之前从较新的jar加载类。

public class ParentLastClassLoader extends ClassLoader { private String jarFile; //Path to the jar file private Hashtable classes = new Hashtable(); //used to cache already defined classes public ParentLastClassLoader(ClassLoader parent, String path) { super(parent); this.jarFile = path; } @Override public Class findClass(String name) throws ClassNotFoundException { System.out.println("Trying to find"); throw new ClassNotFoundException(); } @Override protected synchronized Class loadClass(String className, boolean resolve) throws ClassNotFoundException { System.out.println("Trying to load"); try { System.out.println("Loading class in Child : " + className); byte classByte[]; Class result = null; //checks in cached classes result = (Class) classes.get(className); if (result != null) { return result; } try { JarFile jar = new JarFile(jarFile); JarEntry entry = jar.getJarEntry(className + ".class"); InputStream is = jar.getInputStream(entry); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); int nextValue = is.read(); while (-1 != nextValue) { byteStream.write(nextValue); nextValue = is.read(); } classByte = byteStream.toByteArray(); result = defineClass(className, classByte, 0, classByte.length, null); classes.put(className, result); return result; } catch (Exception e) { throw new ClassNotFoundException(className + "Not found", e); } } catch( ClassNotFoundException e ){ System.out.println("Delegating to parent : " + className); // didn't find it, try the parent return super.loadClass(className, resolve); } } } 

我使用此类加载器在该function中加载了主类,但该function中使用的BouncyCaslte类未由我的自定义类加载器加载。

 ClassLoader loader = new ParentLastClassLoader(Thread.currentThread().getContextClassLoader(), pathToJar); Class myClass = loader.loadClass("MainClassOfTheFeature"); Method mainMethod = myClass.getMethod("MainMethod"); mainMethod.invoke(myClass.getConstructor().newInstance()); 

Jvm仍然使用从旧版本加载的类。 如何在运行该function时让JVM从我的类加载器加载类,并在function未运行时使用旧jar中已加载的旧类?

编辑:在functionMain类的MainMethod中将自定义类加载器设置为Thread上下文类加载器后,问题仍然存在。

 Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); 

我设法解决了这个问题。 修改了ParentLastClassLoader的代码,以获取该function所需的所有Jarfile路径的数组。 因此,当加载类时,将搜索该function所需的所有jar文件以查找.class文件。 如果找不到类文件,则会将其委派给父文件。

  private class ParentLastClassLoader extends ClassLoader { private String[] jarFiles; //Paths to the jar files private Hashtable classes = new Hashtable(); //used to cache already defined classes public ParentLastClassLoader(ClassLoader parent, String[] paths) { super(parent); this.jarFiles = paths; } @Override public Class findClass(String name) throws ClassNotFoundException { System.out.println("Trying to find"); throw new ClassNotFoundException(); } @Override protected synchronized Class loadClass(String className, boolean resolve) throws ClassNotFoundException { System.out.println("Trying to load"); try { System.out.println("Loading class in Child : " + className); byte classByte[]; Class result = null; //checks in cached classes result = (Class) classes.get(className); if (result != null) { return result; } for(String jarFile: jarFiles){ try { JarFile jar = new JarFile(jarFile); JarEntry entry = jar.getJarEntry(className.replace(".","/") + ".class"); InputStream is = jar.getInputStream(entry); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); int nextValue = is.read(); while (-1 != nextValue) { byteStream.write(nextValue); nextValue = is.read(); } classByte = byteStream.toByteArray(); result = defineClass(className, classByte, 0, classByte.length, null); classes.put(className, result); } catch (Exception e) { continue; } } result = (Class) classes.get(className); if (result != null) { return result; } else{ throw new ClassNotFoundException("Not found "+ className); } } catch( ClassNotFoundException e ){ System.out.println("Delegating to parent : " + className); // didn't find it, try the parent return super.loadClass(className, resolve); } } } 

ParentLastClassLoader实例化如下。

 ClassLoader loader = new ParentLastClassLoader(Thread.currentThread().getContextClassLoader(), paths); 

实例化ParentLastClassLoader后,将加载MainClassOfTheFeature并调用其MainMethod

好的,您创建了自己的类加载器,然后使用它加载了一个类。 问题是 – 线程类加载器将如何知道? 因此,您必须使用某个类加载器加载该类,然后将此类加载器设置为线程上下文类加载器。