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

Pau*_*wog 0 java eclipse swing jar classloader

我的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)
Run Code Online (Sandbox Code Playgroud)

编辑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**
Run Code Online (Sandbox Code Playgroud)

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

nIc*_*cOw 5

我真的不知道你的项目的结构,虽然我已经为你做了一个小的示例代码,看看,看看.

考虑一下我的项目 C:\Mine\JAVA\J2SE\src\testingjar>

在我的目录结构内部如下:

                         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
Run Code Online (Sandbox Code Playgroud)

我的类将成为.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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

C:\Mine\JAVA\J2SE\src\testingjar>javac -d classes src\test\CustomPanel.java
Run Code Online (Sandbox Code Playgroud)

现在为JAR文件创建清单文件,其内容如下:

Main-Class: test.CustomPanel
Run Code Online (Sandbox Code Playgroud)

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

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

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

C:\Mine\JAVA\J2SE\src\testingjar\classes>jar -cfm ..\test.jar ..\manifest.txt test
Run Code Online (Sandbox Code Playgroud)

现在使用这个.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();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

C:\Mine\JAVA\J2SE\src\testingjar\classes>java actualtest.ActualImplementation
Run Code Online (Sandbox Code Playgroud)

OUTPUT

JAR实施

匹配这些步骤,可能是你错过了什么,因为它在我这边工作得很好.

最新编辑:如我所知,我反过来做了,现在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

manifest.txt文件的内容将更改如下:

Main-Class: test.CustomFrame
Run Code Online (Sandbox Code Playgroud)

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

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();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

编译顺序与以前非常相似,如下所示:

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
Run Code Online (Sandbox Code Playgroud)

我得到的输出仍然相同.

最新编辑:

我刚刚发现,有时这个东西可以工作,而不是后者,当使用JAR文件时,你必须指定包含点运算符的类路径 .,就像

C:\Mine\JAVA\J2SE\src\testingjar>java -classpath test.jar;.; actualtest.CustomPanel
Run Code Online (Sandbox Code Playgroud)

这是当我把文件夹actualtest package里面的内容testingjar,然后上面的命令适用于这种情况.