首页 > 解决方案 > Eclipse 下的 Java ClassLoader 与 Eclipse 之外的不同

问题描述

我编写了一个简单的插件系统,以便可以包含来自外部资源的扩展。插件管理器从预定的应用程序和用户plugins目录加载插件。我使用自定义URLClassLoader,以便我可以沙箱所有插件。这是一般方法:

  1. 在预定目录之一中找到插件的 JAR 文件。
  2. 如果插件已经加载,则返回已经创建的插件实例。
  3. URLClassLoader如果尚未为包含插件的目录创建自定义实例,请创建一个。
  4. 遍历插件的 JAR 文件中的每个类,PluginInterface通过使用加载该类来查找实现该类的类Class.forName( pluginName, false, PluginClassLoader ),然后测试以查看是否PluginInterface isAssignableFrom加载了类。
  5. 如果 Class 实现了,PluginInterface则创建、初始化并保存 Class 的新实例以供以后使用。

当我在 Eclipse IDE 中运行它时,所有这些都非常有效。但是,当我在 IDE 之外运行它时,检查插件是否实现PluginInterface失败。我相信这是因为在 Eclipse 下,插件和接口都有一个相关的(父或子)ClassLoader(即sun.misc.Launcher$AppClassLoader@73d16e93),而在 Eclipse 之外PluginInterface有一个不相关的ClassLoader(即java.net.URLClassLoader@14ae5a5)。

这是代码:

习俗ClassLoader

public class PROD_PluginClassLoader extends URLClassLoader {

    protected PROD_PluginClassLoader( URL pluginFileUrl ) {
        super( new URL[] { pluginFileUrl } );
    }

    protected PROD_PluginClassLoader( String pluginFilePath ) throws MalformedURLException {
        super( new URL[] { new File( pluginFilePath ).toURI().toURL() } );
    }

    protected PROD_PluginClassLoader( URL[] pluginFileUrls ) {
        super( pluginFileUrls );
    }

}

PluginLoader:_

    private static List< String > loadedPlugins = new ArrayList<>();
    private static List< PROD_PluginInterface > plugins = new ArrayList<>();
    private static Map< String, PROD_PluginClassLoader > pluginClassLoaders = new HashMap<>();

    protected static void getPluginInstance( String pluginName, String pluginFilePath ) {
        try {
            PROD_Utilities.printDebug( "Loading plugin name(" + pluginName + ") from(" + pluginFilePath + ")" );
            if ( !pluginClassLoaders.containsKey( pluginFilePath ) ) pluginClassLoaders.put( pluginFilePath, new PROD_PluginClassLoader( pluginFilePath ) );
            PROD_PluginClassLoader pLoader = pluginClassLoaders.get( pluginFilePath );
            boolean pluginLoaded = false;
            for ( String n : PROD_Utilities.getClassNamesFromJarFile( pluginFilePath ) ) {
                Class<?> pClass = Class.forName( n, false, pLoader );
                String interfaces = "";
                for ( Class<?> c : pClass.getInterfaces() ) interfaces += "," + c.getName();
                if ( !interfaces.isEmpty() ) interfaces = interfaces.substring( 1 );
                PROD_Utilities.printDebug( String.format( "Plugin name(%s) object class(%s) super(%s) interfaces(%s) isPlugin(%b)", pluginName, pClass.getName(), pClass.getSuperclass().getName(), interfaces, PROD_PluginInterface.class.isAssignableFrom( pClass ) ) );
if ( pClass.getInterfaces().length > 0 )
PROD_Utilities.printDebug(
String.format(
     "pClass loader(%s) parent(%s) pClass interface loader(%s) parent(%s) PROD_PluginInterface loader(%s) parent(%s)"
    ,pClass.getClassLoader()
    ,pClass.getClassLoader().getParent()
    ,pClass.getInterfaces()[0].getClassLoader()
    ,pClass.getInterfaces()[0].getClassLoader().getParent()
    ,PROD_PluginInterface.class.getClassLoader()
    ,PROD_PluginInterface.class.getClassLoader().getParent()
));
                if ( PROD_PluginInterface.class.isAssignableFrom( pClass ) ) {
                    Class<? extends PROD_Plugin> newClass = pClass.asSubclass( PROD_Plugin.class );
                    Constructor<?> constructor = newClass.getConstructor();
                    setPluginSandbox();
                    plugins.add( ( PROD_PluginInterface ) constructor.newInstance() );
                    plugins.get( plugins.size()-1 ).pluginInitialization();
                    unsetPluginSandbox();
                    pluginLoaded = true;
                }
            }
            if ( pluginLoaded ) loadedPlugins.add( pluginName.toLowerCase() );
            else PROD_Utilities.printError( "Plugin (" + pluginName + ") is not a valid PROD plugin." );
        } catch ( InstantiationException    | IllegalAccessException | IllegalArgumentException
                | InvocationTargetException | ClassNotFoundException | NoSuchMethodException 
                | SecurityException         | MalformedURLException     e ) {
            PROD_Utilities.printError( "Could not load plugin (" + pluginName + ").", e.getMesprod() );
        }
    }

在Eclipse下运行时的调试信息:

 Debug: PROD_PluginManager.getPluginInstance().line(138): Loading plugin name(proddb) from(C:\Users\userid\eclipse-workspace\prod\plugins\proddb.jar)
 Debug: PROD_PluginManager.getPluginInstance().line(147): Plugin name(proddb) object class(com.company.prod.proddb.PRODDB) super(com.company.prod.PROD_Plugin) interfaces(com.company.prod.PROD_PluginInterface) isPlugin(true)
 Debug: PROD_PluginManager.getPluginInstance().line(149): pClass loader(com.company.prod.PROD_PluginClassLoader@5ec0a365) parent(sun.misc.Launcher$AppClassLoader@73d16e93) pClass interface loader(sun.misc.Launcher$AppClassLoader@73d16e93) parent(sun.misc.Launcher$ExtClassLoader@55f96302) PROD_PluginInterface loader(sun.misc.Launcher$AppClassLoader@73d16e93) parent(sun.misc.Launcher$ExtClassLoader@55f96302)

在 Eclipse 之外运行时的调试信息:

 Debug: PROD_PluginManager.getPluginInstance().line(138): Loading plugin name(proddb) from(C:\Users\userid\eclipse-workspace\prod\plugins\proddb.jar)
 Debug: PROD_PluginManager.getPluginInstance().line(147): Plugin name(proddb) object class(com.company.prod.proddb.PRODDB) super(com.company.prod.PROD_Plugin) interfaces(com.company.prod.PROD_PluginInterface) isPlugin(false)
 Debug: PROD_PluginManager.getPluginInstance().line(149): pClass loader(com.company.prod.PROD_PluginClassLoader@12405818) parent(sun.misc.Launcher$AppClassLoader@55f96302) pClass interface loader(sun.misc.Launcher$AppClassLoader@55f96302) parent(sun.misc.Launcher$ExtClassLoader@3d3fcdb0) PROD_PluginInterface loader(java.net.URLClassLoader@14ae5a5) parent(null)

我觉得奇怪的是PluginInterface ClassLoader已更改为URLClassLoader.

我认为问题在于PluginInterface和插件共享相关的ClassLoader,因此插件PluginInterface在技术上是与应用程序不同的 Java 接口PluginInterface。如果该评估是正确的,那么我的问题是如何解决这个问题,以便PluginInterface和插件共享一个相关的ClassLoader

或者,也许我的评估是不正确的。在这种情况下,我的问题是为什么插件似乎没有实现PluginInterface

我已经为此苦苦挣扎了好几天了,所以提前感谢任何和所有的答案。

编辑

我的代码(不是插件)是如何加载的?

从 Eclipse 内部:使用 EclipseRun -> Run菜单选项。

从 Eclipse 外部:java -jar prod.jar

标签: javaeclipseinterfaceclassloader

解决方案


很长一段时间后,我终于想通了,并在这里发布我的发现,以防其他人遇到类似的问题isAssignableFrom

当使用 Java 的导出向导->可运行 JAR 文件从 Eclipse 导出程序时,我选择了库处理选项将所需的库打包到生成的 JAR中,并且isAssignableFrom如原始帖子中所述出现故障。使用库处理选项重新导出后,将所需的库提取到生成的 JAR中,一切都按预期工作。


推荐阅读