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

我的Java应用程序有一个插件系统。 我使用URL类加载器调用外部类。 当我的应用程序作为类文件运行时,以及当我的应用程序处于JARforms时,该部分运行良好。 我遇到的问题是,插件文件可以运行他们自己的独立代码,但他们创建了一个JPanel。 当我尝试将JPanel添加到主应用程序类中的JPanel时,我得到一个引用主类的空指针exception。 (com.cpcookieman.app.Main)但是,如果我运行应用程序的类文件,只有在打包时才会发生这种情况。 我怎么解决这个问题?

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

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

java.lang.NullPointerException at TestPlugin2.Main.(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是主要的应用程序,如果有人对此感到困惑。

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

考虑我的项目位于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 

我的类将成为.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文件创建清单文件,其内容如下:

 Main-Class: test.CustomPanel 

请记住冒号(:)和包名称之间的空格即测试,在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 

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); } } 

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文件夹中时,上面的命令适用于这种情况。