父类最后一个类加载器来解决Java类路径地狱?

父类最后一个类加载器来解决Java类路径地狱?,java,classpath,classloader,bouncycastle,Java,Classpath,Classloader,Bouncycastle,我有一个项目,它使用两个版本的bouncyCastle jars bcprov-jdk15和bcprov-jdk16。jvm加载旧版本,但我编写了一个功能,需要运行新版本。我试图通过使用自定义类加载器来解决这个类路径地狱。在一些Google搜索之后,在一些以前的Stackoverflow答案的帮助下,我编写了下面的Parent Last Class loader,以便在委托给父类装入器之前从较新的jar装入类 public class ParentLastClassLoader extends

我有一个项目,它使用两个版本的bouncyCastle jars bcprov-jdk15和bcprov-jdk16。jvm加载旧版本,但我编写了一个功能,需要运行新版本。我试图通过使用自定义类加载器来解决这个类路径地狱。在一些Google搜索之后,在一些以前的Stackoverflow答案的帮助下,我编写了下面的Parent Last Class loader,以便在委托给父类装入器之前从较新的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);
        }
    }
}
Jvm仍然使用从旧版本加载的类。如何使JVM在运行该功能时从我的类加载器加载类,并在该功能未运行时使用旧jar中已加载的旧类

编辑: 即使将自定义类加载器设置为feature Main类的Main方法中的线程上下文类加载器,问题仍然存在

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

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

我设法解决了这个问题。修改了ParentLastClassLoader的代码,以获得该功能所需的所有Jarfile路径的数组。因此,当加载一个类时,将搜索该功能所需的所有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被实例化,将加载MainClassofFeature,并调用其MainMethod

我还没有完全理解这是如何工作的,或者为什么需要它,但这是目前我能够自动链接jar中其他类的唯一方法。
    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);
        }
    }
}
ClassLoader loader = new ParentLastClassLoader(Thread.currentThread().getContextClassLoader(), paths);