Java:为什么打包成jar文件的代码会阻止外部类访问? [英] Java: why code packaged into a jar file prevent external classes from accessing?

查看:20
本文介绍了Java:为什么打包成jar文件的代码会阻止外部类访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a plugin system for my Java app. I invoke an external class with the URL class loader. That part works well, both when my application is run as class files, and when my application is in JAR form. The problem that I am running into is, the plugin files can run their own independent code just fine, but they create a JPanel. When I try to add that JPanel to the JPanel that's in the main application class, I get a null pointer exception referencing the main class. (com.cpcookieman.app.Main) But that doesn't happen if I run the class files for the application, only when it's packaged. How can I solve that?

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

Why does my code being packaged into a jar file prevent external classes from accessing classes inside the 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 是主要的应用程序,如果有人对此感到困惑的话.

The plugin loads up using it's constructor, and creates a panel to be added to the frame that's inside one of the classes inside the JAR file. The JAR is the main application, if anyone's confused about that.

推荐答案

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

I really don't know the structure of your project, though I had made a small example code for you to look at, have a look.

考虑我的项目放在C:MineJAVAJ2SEsrc estingjar>

里面我的目录结构如下:

Inside this my directory structure is as below :

                         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 文件的一部分,如下所示:

My Class that will become a part of a .jar file, is as given below :

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:MineJAVAJ2SEsrc	estingjar>javac -d classes src	estCustomPanel.java

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

Main-Class: test.CustomPanel

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

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

Now for creating a JAR File named test.jar, I wrote these commands :

C:MineJAVAJ2SEsrc	estingjar>cd classes

C:MineJAVAJ2SEsrc	estingjarclasses>jar -cfm ..	est.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:MineJAVAJ2SEsrc	estingjarclasses>cd..

C:MineJAVAJ2SEsrc	estingjar>javac -classpath test.jar -d classes srcactualtestActualImplement
ation.java

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

C:MineJAVAJ2SEsrc	estingjar>cd classes

C:MineJAVAJ2SEsrc	estingjarclasses>java actualtest.ActualImplementation

输出

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

Do match the steps, might be you be missing something, since it's working fine on my side.

最新正如所问的那样,我反过来做了,现在 JFrame 在 .jar 文件中,JPanel 正在使用它.强>

LATEST EDIT : As asked, I did that the other way around, now JFrame is inside the .jar file and JPanel is using it.

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

The class that will become the part of the .jar file is as follows :

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 类的类如下:

And the class which will use CustomFrame Class from the .jar file, is as follows :

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

编译顺序,和之前大同小异,如下:

The compilation sequence, is very much the same as before, which is as follows :

C:MineJAVAJ2SEsrc	estingjar>javac -d classes src	estCustomFrame.java

C:MineJAVAJ2SEsrc	estingjar>cd classes

C:MineJAVAJ2SEsrc	estingjarclasses>jar -cfm ..	est.jar ..manifest.txt test

C:MineJAVAJ2SEsrc	estingjarclasses>cd..

C:MineJAVAJ2SEsrc	estingjar>javac -classpath test.jar -d classes srcactualtestCustomPanel.jav
a

C:MineJAVAJ2SEsrc	estingjar>cd classes

C:MineJAVAJ2SEsrc	estingjarclasses>java actualtest.CustomPanel

仍然是相同的输出,我得到了.

Still the same output, I am getting.

最新

我刚刚发现,有时这件事有效,而不是后者,当使用 JAR 文件时,您必须指定包含点运算符的类路径以及 .,如

I just found out, that sometimes this thing works, instead of the later, when using JAR Files, you have to specify the classpath including the dot operator as well ., like

C:MineJAVAJ2SEsrc	estingjar>java -classpath test.jar;.; actualtest.CustomPanel

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

This is when I brought the actualtest package inside the testingjar Folder, then the above command worked for this situation.

这篇关于Java:为什么打包成jar文件的代码会阻止外部类访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆