2

bouncyCastle jar bcprov-jdk15 と bcprov-jdk16 の 2 つのバージョンを使用するプロジェクトがあります。jvm は古いバージョンをロードしますが、新しいバージョンを実行する必要がある私が書いた機能があります。カスタムクラスローダーを使用して、このクラスパス地獄を解決しようとしました。いくつかのグーグル検索の後、いくつかの以前の Stackoverflow の回答[1] [2]とこのブログの助けを借りて、親クラス ローダーに委譲する前に新しい jar からクラスをロードするために、次のParent Last Class ローダーを作成しました。

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.currentThread().setContextClassLoader(this.getClass().getClassLoader());
4

2 に答える 2

4

私はこの問題を解決することができました。機能に必要なすべての Jarfile パスの配列を取得するように、 ParentLastClassLoaderのコードを変更しました。そのため、クラスがロードされると、機能に必要なすべての 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が呼び出されます。

于 2013-07-23T10:01:38.723 に答える
1

わかりました。独自のクラスローダーを作成し、それを使用してクラスをロードしました。問題は、スレッド クラスローダがそれをどのように認識するかということです。したがって、何らかのクラスローダを使用してクラスをロードし、このクラスローダをスレッド コンテキスト クラスローダとして設定する必要があります。

于 2013-07-21T18:44:55.863 に答える