0

Javaアプリ用のプラグインシステムがあります。URLクラスローダーを使用して外部クラスを呼び出します。この部分は、アプリケーションがクラスファイルとして実行されている場合と、アプリケーションがJAR形式で実行されている場合の両方でうまく機能します。私が遭遇している問題は、プラグインファイルが独自の独立したコードを問題なく実行できることですが、JPanelを作成します。そのJPanelをメインアプリケーションクラスにあるJPanelに追加しようとすると、メインクラスを参照するnullポインタ例外が発生します。(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ファイル内のクラスの1つ内にあるフレームに追加されるパネルを作成します。誰かがそれについて混乱している場合は、JARがメインアプリケーションです。

4

1 に答える 1

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

.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

コロン (:) とパッケージ名 (test など) の間のスペースを覚えておいてください。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

この .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

出力

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

于 2012-06-22T04:41:47.993 に答える