【问题标题】:Parent Last Classloader to solve Java Class path hell?Parent Last Classloader 解决 Java 类路径地狱?
【发布时间】:2013-07-20 11:32:02
【问题描述】:

我有一个项目使用两个版本的 bouncyCastle 罐子 bcprov-jdk15 和 bcprov-jdk16。 jvm 会加载旧版本,但我编写的一个功能需要更新版本才能运行。我试图通过使用自定义类加载器来解决这个类路径地狱。经过一番谷歌搜索并借助一些以前的 Stackoverflow 答案[1][2] 和这个blog,我编写了以下 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);
        }
    }
}

我使用此类加载器加载了功能中的主类,但我的自定义类加载器未加载该功能中使用的 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 仍然使用它从旧版本加载的类。如何让 JVM 在运行该功能时从我的类加载器加载类,并在该功能未运行时使用旧 jar 中已加载的旧类?

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

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

【问题讨论】:

    标签: java classpath classloader bouncycastle


    【解决方案1】:

    我设法解决了这个问题。修改 ParentLastClassLoader 的代码以获取该功能所需的所有 Jarfile 路径 的数组。因此,当一个类被加载时,该功能所需的所有 jarfile 都将被搜索以查找 .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

    【讨论】:

    • 我还没有完全理解它是如何工作的或者为什么需要它,但这是目前我能够在我的 jar 中自动链接其他类的唯一方法。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2013-02-14
      • 1970-01-01
      • 2016-07-19
      • 1970-01-01
      • 2018-06-25
      • 2010-09-27
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多