【问题标题】:Java: why code packaged into a jar file prevent external classes from accessing?Java:为什么打包成 jar 文件的代码会阻止外部类访问?
【发布时间】:2012-06-24 09:07:52
【问题描述】:

我的 Java 应用程序有一个插件系统。我使用 URL 类加载器调用一个外部类。这部分运行良好,无论是当我的应用程序作为类文件运行时,还是当我的应用程序是 JAR 形式时。我遇到的问题是,插件文件可以很好地运行自己的独立代码,但它们会创建一个 JPanel。当我尝试将该 JPanel 添加到主应用程序类中的 JPanel 时,我得到一个引用主类的空指针异常。 (com.cpcookieman.app.Main) 但是,如果我运行应用程序的类文件,则不会发生这种情况,只有当它被打包时。我该如何解决?

为什么我的代码被打包到 jar 文件中会阻止外部类访问 jar 中的类?

编辑:根据要求,堆栈跟踪。

java.lang.NullPointerException
    at TestPlugin2.Main.<init>(Main.java:23)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at java.lang.Class.newInstance0(Unknown Source)
    at java.lang.Class.newInstance(Unknown Source)
    at com.cpcookieman.ph.PluginLoader$2.run(PluginLoader.java:74)
    at java.lang.Thread.run(Unknown Source)

编辑 2:类加载代码

    String pluginsPath;
    if (Main.os.equals("Windows"))
    {
        pluginsPath = "C:\\plugins\\";
    }
    else
    {
        pluginsPath = "~/plugins/";
    }
    File file = new File(pluginsPath);
    if (!file.exists())
    {
        System.out.println("DEBUG: Plugin path could not be found!");
    }
    try
    {
        URL url = file.toURI().toURL();
        final URL[] urls = new URL[]{url};
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    ClassLoader cl = new URLClassLoader(urls);
                    Class plugin = cl.loadClass(text + ".Main");
                    plugin.newInstance();
                    close();
                }
                **snip catch statements**
            }
        }).start();
    }
    **snip catch statements**

插件使用它的构造函数加载,并创建一个面板以添加到 JAR 文件中的一个类中的框架中。 JAR 是主要应用程序,如果有人对此感到困惑的话。

【问题讨论】:

  • “Wut”?也许他们在 CP 中没有 JAR? (但是,在大多数情况下,NPE 是一个不同的问题...因为类加载失败会导致经常杀死 VM 的 Nasty-Non-NPE-Exception。也许应该调查一下而不是责备类加载器/加载 :-)
  • JAR 是否需要在某个文件夹中才能将其包含在类路径中?如果是这样,JAR 是否可以自我调整以将自己放入类路径中?
  • JAR 必须在 in 类路径中;它不能把自己放在那里。 (还要确保类/类没有/没有被使用的陈旧版本......因为这可能是一件令人讨厌的事情。)
  • 你能发布堆栈跟踪吗?
  • 您将不得不发布一些代码,从 TestPlugin2.Main.java 开始。更好的是发布所有内容或一个自包含的小例子来展示问题 - HTH

标签: java eclipse swing jar classloader


【解决方案1】:

我真的不知道你项目的结构,虽然我做了一个小例子代码给你看,看看吧。

考虑一下我的项目放在C:\Mine\JAVA\J2SE\src\testingjar&gt;

在这个里面我的目录结构如下:

                         testingjar
                             |
              ----------------------------------
             |          |           |          |
          classes      src    manifest.text  test.jar(this .jar we be creating shortly)
             |          |
             |       (Almost same as classes folder, just .java files)
      ---------------
      |             |
  actualtest       test
      |             |
   *.class       *.class

我的类将成为 .jar 文件的一部分,如下所示:

package test;

import java.awt.*;
import javax.swing.*;

public class CustomPanel extends JPanel
{
    public CustomPanel()
    {
        setOpaque(true);
        setBackground(Color.DARK_GRAY);

        JLabel label = new JLabel(
            "I am a JLabel from Java Swing", JLabel.CENTER);
        label.setOpaque(false); 
        label.setForeground(Color.WHITE);
        add(label); 
    }

    @Override
    public Dimension getPreferredSize()
    {
        return (new Dimension(500, 300));
    }
}

我用以下命令编译了这个类:

C:\Mine\JAVA\J2SE\src\testingjar>javac -d classes src\test\CustomPanel.java

现在为JAR文件创建Manifest File,内容如下:

Main-Class: test.CustomPanel

请记住冒号 (:) 和包名(即 test)之间的空格,在 CustomPanel 之后,按 Enter 并保存文件。

现在为了创建一个名为 test.jar 的 JAR 文件,我编写了以下命令:

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>jar -cfm ..\test.jar ..\manifest.txt test

现在将使用这个 .jar 文件的类将是这样的:

package actualtest;

import test.CustomPanel;

import java.awt.*;
import javax.swing.*;

public class ActualImplementation
{
    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("Testing Jar Implementation");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        CustomPanel panel = new CustomPanel();
        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new ActualImplementation().createAndDisplayGUI();
            }
        });
    }
}

我通过编写这些命令来编译它:

C:\Mine\JAVA\J2SE\src\testingjar\classes>cd..

C:\Mine\JAVA\J2SE\src\testingjar>javac -classpath test.jar -d classes src\actualtest\ActualImplement
ation.java

现在运行我写了这些命令:

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>java actualtest.ActualImplementation

输出

请按照步骤进行,可能是您遗漏了什么,因为它在我这边工作正常。

最新编辑:如被问及,我反其道而行之,现在 JFrame 在 .jar 文件中,JPanel 正在使用它。

将成为 .jar 文件一部分的类如下:

package test;

import java.awt.*;
import javax.swing.*;

public class CustomFrame extends JFrame
{
    public CustomFrame(String title)
    {
        super(title);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
}

manifest.txt 文件的内容,会发生如下变化:

Main-Class: test.CustomFrame

而将使用.jar 文件中的CustomFrame 类的类如下:

package actualtest;

import test.CustomFrame;

import java.awt.*;
import javax.swing.*;


// http://stackoverflow.com/a/11150286/1057230
public class CustomPanel extends JPanel
{
    private CustomFrame frame;

    public CustomPanel()
    {
        setOpaque(true);
        setBackground(Color.DARK_GRAY);

        JLabel label = new JLabel(
            "I am a JLabel from Java Swing", JLabel.CENTER);
        label.setOpaque(false); 
        label.setForeground(Color.WHITE);
        add(label);                 
    }

    private void createAndDisplayGUI()
    {
        frame = new CustomFrame("Testing Jar Implementation");
        frame.setContentPane(this);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    @Override
    public Dimension getPreferredSize()
    {
        return (new Dimension(500, 300));
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new CustomPanel().createAndDisplayGUI();
            }
        });
    }
}

编译顺序,和之前大体相同,如下:

C:\Mine\JAVA\J2SE\src\testingjar>javac -d classes src\test\CustomFrame.java

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>jar -cfm ..\test.jar ..\manifest.txt test

C:\Mine\JAVA\J2SE\src\testingjar\classes>cd..

C:\Mine\JAVA\J2SE\src\testingjar>javac -classpath test.jar -d classes src\actualtest\CustomPanel.jav
a

C:\Mine\JAVA\J2SE\src\testingjar>cd classes

C:\Mine\JAVA\J2SE\src\testingjar\classes>java actualtest.CustomPanel

还是一样的输出,我得到了。

最新编辑:

我刚刚发现,有时这个东西会起作用,而不是后来,当使用 JAR 文件时,你必须指定包含点运算符的类路径以及.,比如

C:\Mine\JAVA\J2SE\src\testingjar>java -classpath test.jar;.; actualtest.CustomPanel

这是当我将actualtest package 放入testingjar 文件夹时,上面的命令适用于这种情况。

【讨论】:

  • 我很难理解你在做什么,但我认为你把它倒过来了。我在 jar 文件中有框架,jar 文件加载了带有面板的类文件,此时该类尝试将其面板添加到 jar 的框架中。您似乎在 jar 文件中有面板,并且框架正在由外部类加载。使数据流向那个方向我没有问题。
  • 它仍在工作,如果我反过来,请立即查看此编辑。
  • 您的代码没有使用任何形式的动态类加载。由于我正在创建一个插件功能,因此我不知道开发人员正在创建什么或将命名什么。因此,它不能导入我的主应用程序或以与您实例化它相同的方式实例化。我将尝试在主帖中发布一个小代码示例,说明我的课程是如何加载的。
猜你喜欢
  • 2011-06-16
  • 2011-01-15
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-15
  • 1970-01-01
相关资源
最近更新 更多