Graalvm編譯swing桌面應用失敗問題解決

前文介紹了Graalvm安裝和靜態編譯,但是直接編譯java swing桌面應用時是可能會出錯。
當前最新版本爲GraalVM22.0.0.2,支持jdk11和jdk17,看了官方github的介紹,當前是建議使用jdk11的graalvm來編譯swing。
本文使用的版本爲graalvm-ce-java11-21.3.0,win10系統,環境已經按前文配置好。

HelloWorld1.java測試代碼

package test.aaa.graalvm;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class HelloWorld1
{
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame jFrame = new JFrame("測試Graalvm編譯swing_penngo");
            JTextField textField = new JTextField();
//            textField.addKeyListener(new KeyAdapter() {
//                @Override
//                public void keyPressed(KeyEvent e) {
//                    super.keyPressed(e);
//                }
//            });
            jFrame.add(textField, BorderLayout.NORTH);
            JButton button = new JButton("penngo");
            button.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    super.mouseClicked(e);
                }
            });
            jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jFrame.add(button);
            jFrame.setSize(400,300);
            jFrame.setVisible(true);
        });
    }
}

編譯腳本native_build1.bat

SET class_path=test_graalvm-1.0-SNAPSHOT.jar;
native-image -classpath %class_path% test.aaa.graalvm.HelloWorld1

命令行下運行

D:\project\idea\test_graalvm\target>native_build1.bat
[test.aaa.graalvm.helloworld1:23548]    classlist:   1,700.60 ms,  0.96 GB
[test.aaa.graalvm.helloworld1:23548]        (cap):   2,574.44 ms,  0.96 GB
[test.aaa.graalvm.helloworld1:23548]        setup:   4,154.42 ms,  0.96 GB
[test.aaa.graalvm.helloworld1:23548]     (clinit):     328.39 ms,  3.10 GB
[test.aaa.graalvm.helloworld1:23548]   (typeflow):   3,273.02 ms,  3.10 GB
[test.aaa.graalvm.helloworld1:23548]    (objects):   9,498.57 ms,  3.10 GB
[test.aaa.graalvm.helloworld1:23548]   (features):   2,513.36 ms,  3.10 GB
[test.aaa.graalvm.helloworld1:23548]     analysis:  16,540.22 ms,  3.10 GB
[test.aaa.graalvm.helloworld1:23548]     universe:   1,161.66 ms,  3.16 GB
[test.aaa.graalvm.helloworld1:23548]      (parse):     915.07 ms,  3.16 GB
[test.aaa.graalvm.helloworld1:23548]     (inline):   2,203.65 ms,  3.78 GB
[test.aaa.graalvm.helloworld1:23548]    (compile):  12,583.72 ms,  3.99 GB
[test.aaa.graalvm.helloworld1:23548]      compile:  17,002.37 ms,  3.99 GB
[test.aaa.graalvm.helloworld1:23548]        image:   1,515.12 ms,  3.99 GB
[test.aaa.graalvm.helloworld1:23548]        write:     668.16 ms,  3.99 GB
[test.aaa.graalvm.helloworld1:23548]      [total]:  42,962.41 ms,  3.99 GB
# Printing build artifacts to: D:\project\idea\test_graalvm\target\test.aaa.graalvm.helloworld1.build_artifacts.txt

編譯是成功的,運行test.aaa.graalvm.helloworld1.exe,報錯誤

D:\project\idea\test_graalvm\target>test.aaa.graalvm.helloworld1.exe
Exception in thread "main" java.awt.AWTError: Toolkit not found: sun.awt.windows.WToolkit
        at java.awt.Toolkit$2.run(Toolkit.java:595)
        at java.awt.Toolkit$2.run(Toolkit.java:583)
        at java.security.AccessController.doPrivileged(AccessController.java:87)
        at java.awt.Toolkit.getDefaultToolkit(Toolkit.java:582)
        at java.awt.Toolkit.getEventQueue(Toolkit.java:1494)
        at java.awt.EventQueue.invokeLater(EventQueue.java:1312)
        at javax.swing.SwingUtilities.invokeLater(SwingUtilities.java:1421)
        at test.aaa.graalvm.HelloWorld1.main(HelloWorld1.java:14)

出現錯誤的原因是native-image漏掉一部分jni庫和一部分通過反射執行的庫。對於上邊swing編譯錯誤,可以使用兩種方法解決。

方法一:

對於這種錯誤問題,GraalVM提供了reflect-config.json、jni-config.json、resource-config.json、proxy-config.json、serialization-config.json和predefined-classes-config.json這6個json格式的配置文件,分別用於向靜態編譯提供反射目標信息、JNI回調目標信息、資源文件信息、動態代理目標接口信息、序列化信息和提前定義的動態類信息。
同時還提供native-image-agent工具,通過預運行,在運行時監控並記錄與這些動態特性相關的函數調用信息,並保存到上邊6個文件。

使用native-image-agent預運行,收集相關運行依賴文件,需要把所有功能都點擊執行一次。
class_build.bat

set GRAALVM_HOME=C:\Progra~1\Java\graalvm\graalvm-ce-java11-21.3.0
SET class_path=test_graalvm-1.0-SNAPSHOT.jar;
%GRAALVM_HOME%/bin/java -classpath %class_path% -agentlib:native-image-agent=config-output-dir=META-INF/native-image test.aaa.graalvm.HelloWorld1

命令行運行class_build.bat

META-INF/native-image目錄生成的json文件

native-image默認會從當前路徑加載META-INF/native-image目錄內的配置文件
重新運行native_build1.bat

編譯完成後,運行test.aaa.graalvm.helloworld1.exe,會報下面錯誤

D:\project\idea\test_graalvm\target>test.aaa.graalvm.helloworld1.exe
Exception in thread "AWT-EventQueue-0" java.lang.InternalError: java.lang.reflect.InvocationTargetException
        at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:86)
        at java.security.AccessController.doPrivileged(AccessController.java:87)
        at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
        at sun.font.SunFontManager.getInstance(SunFontManager.java:249)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:265)
        at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:1243)
        at javax.swing.JComponent.getFontMetrics(JComponent.java:1646)
        at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:356)
        at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:328)
        at javax.swing.plaf.basic.BasicButtonUI.paint(BasicButtonUI.java:307)
        at javax.swing.plaf.metal.MetalButtonUI.update(MetalButtonUI.java:175)
        at javax.swing.JComponent.paintComponent(JComponent.java:797)
        at javax.swing.JComponent.paint(JComponent.java:1074)
        at javax.swing.JComponent.paintChildren(JComponent.java:907)
        at javax.swing.JComponent.paint(JComponent.java:1083)
        at javax.swing.JComponent.paintChildren(JComponent.java:907)
        at javax.swing.JComponent.paint(JComponent.java:1083)
        at javax.swing.JLayeredPane.paint(JLayeredPane.java:590)
        at javax.swing.JComponent.paintChildren(JComponent.java:907)
        at javax.swing.JComponent.paintToOffscreen(JComponent.java:5262)
        at javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1643)
        at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1618)
        at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1556)
        at javax.swing.RepaintManager.paint(RepaintManager.java:1323)
        at javax.swing.JComponent.paint(JComponent.java:1060)
        at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
        at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:78)
        at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:115)
        at java.awt.Container.paint(Container.java:2002)
        at java.awt.Window.paint(Window.java:3940)
        at javax.swing.RepaintManager$4.run(RepaintManager.java:876)
        at javax.swing.RepaintManager$4.run(RepaintManager.java:848)
        at java.security.AccessController.doPrivileged(AccessController.java:105)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:848)
        at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:823)
        at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:772)
        at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1890)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
        at java.awt.EventQueue$4.run(EventQueue.java:721)
        at java.awt.EventQueue$4.run(EventQueue.java:715)
        at java.security.AccessController.doPrivileged(AccessController.java:105)
        at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:596)
        at com.oracle.svm.core.windows.WindowsJavaThreads.osThreadStartRoutine(WindowsJavaThreads.java:138)
Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:84)
        ... 52 more
Caused by: java.lang.Error: java.home property not set
        at sun.awt.FontConfiguration.findFontConfigFile(FontConfiguration.java:182)
        at sun.awt.FontConfiguration.<init>(FontConfiguration.java:99)
        at sun.awt.windows.WFontConfiguration.<init>(WFontConfiguration.java:41)
        at sun.awt.Win32FontManager.createFontConfiguration(Win32FontManager.java:179)
        at sun.font.SunFontManager$2.run(SunFontManager.java:379)
        at java.security.AccessController.doPrivileged(AccessController.java:87)
        at sun.font.SunFontManager.<init>(SunFontManager.java:324)
        at sun.awt.Win32FontManager.<init>(Win32FontManager.java:87)
        ... 54 more
......

新建conf\fonts目錄,複製graalvm-ce-java11-21.3.0\lib\fontconfig.bfc文件到D:\project\idea\test_graalvm\target\conf\fonts目錄
重新運行並加上-Djava.home=.參數

test.aaa.graalvm.helloworld1.exe -Djava.home=.

方法2:使用一些有調用jni相關依賴庫,比如jna包,或上一篇文用的jcef包(https://my.oschina.net/penngo/blog/5455479)也行
pom.xml加入兩個jna依賴包

在程序啓動時,必須調用到jna的代碼,native-image才能在編譯時發現需要用jni等相關依賴庫,並編譯到二進制包中
HelloWorld2.java測試代碼

package test.aaa.graalvm;
import com.sun.jna.platform.win32.User32;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class HelloWorld2
{
    static{
        User32 lib = User32.INSTANCE;
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame jFrame = new JFrame("測試Graalvm編譯swing_penngo");
            JTextField textField = new JTextField();
            textField.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    super.keyPressed(e);
                }
            });
            jFrame.add(textField, BorderLayout.NORTH);
            JButton button = new JButton("penngo");
            button.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    super.mouseClicked(e);
                }
            });
            jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jFrame.add(button);
            jFrame.setSize(400,300);
            jFrame.setVisible(true);
        });
    }
}

編譯腳本native_build2.bat

SET class_path=test_graalvm-1.0-SNAPSHOT.jar;lib\jna-5.6.0.jar;lib\jna-platform-5.6.0.jar;
native-image -classpath %class_path% test.aaa.graalvm.HelloWorld2

命令行下運行native_build2.bat,注意需要先刪除上邊生成的META-INF\native-image目錄和文件。

D:\project\idea\test_graalvm\target>native_build2.bat
D:\project\idea\test_graalvm\target>SET class_path=test_graalvm-1.0-SNAPSHOT.jar;lib\jna-5.6.0.jar;lib\jna-platform-5.6.0.jar;
D:\project\idea\test_graalvm\target>native-image -classpath test_graalvm-1.0-SNAPSHOT.jar;lib\jna-5.6.0.jar;lib\jna-platform-5.6.0.jar; test.aaa.graalvm.HelloWorld2
[test.aaa.graalvm.helloworld2:12476]    classlist:   1,836.39 ms,  0.96 GB
[test.aaa.graalvm.helloworld2:12476]        (cap):   2,672.16 ms,  0.96 GB
[test.aaa.graalvm.helloworld2:12476]        setup:   4,203.58 ms,  0.96 GB
[test.aaa.graalvm.helloworld2:12476]     (clinit):     246.45 ms,  2.66 GB
[test.aaa.graalvm.helloworld2:12476]   (typeflow):   2,525.57 ms,  2.66 GB
[test.aaa.graalvm.helloworld2:12476]    (objects):   5,305.92 ms,  2.66 GB
[test.aaa.graalvm.helloworld2:12476]   (features):   1,693.64 ms,  2.66 GB
[test.aaa.graalvm.helloworld2:12476]     analysis:  10,335.40 ms,  2.66 GB
[test.aaa.graalvm.helloworld2:12476]     universe:     971.94 ms,  2.71 GB
Warning: Dynamic proxy method java.lang.reflect.Proxy.newProxyInstance invoked at com.sun.jna.Native.load(Native.java:598)
Warning: Aborting stand-alone image build due to dynamic proxy use without configuration.
Warning: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
[test.aaa.graalvm.helloworld2:12476]      [total]:  17,504.01 ms,  2.71 GB
# Printing build artifacts to: D:\project\idea\test_graalvm\target\test.aaa.graalvm.helloworld2.build_artifacts.txt
[test.aaa.graalvm.helloworld2:8648]    classlist:   1,658.41 ms,  0.96 GB
[test.aaa.graalvm.helloworld2:8648]        (cap):   2,695.81 ms,  0.96 GB
[test.aaa.graalvm.helloworld2:8648]        setup:   4,204.51 ms,  0.96 GB
[test.aaa.graalvm.helloworld2:8648]     (clinit):     133.06 ms,  1.72 GB
[test.aaa.graalvm.helloworld2:8648]   (typeflow):   1,913.65 ms,  1.72 GB
[test.aaa.graalvm.helloworld2:8648]    (objects):   1,686.67 ms,  1.72 GB
[test.aaa.graalvm.helloworld2:8648]   (features):     659.56 ms,  1.72 GB
[test.aaa.graalvm.helloworld2:8648]     analysis:   4,670.54 ms,  1.72 GB
[test.aaa.graalvm.helloworld2:8648]     universe:     565.01 ms,  1.76 GB
[test.aaa.graalvm.helloworld2:8648]      (parse):     369.10 ms,  1.76 GB
[test.aaa.graalvm.helloworld2:8648]     (inline):     539.90 ms,  2.33 GB
[test.aaa.graalvm.helloworld2:8648]    (compile):   4,614.19 ms,  2.98 GB
[test.aaa.graalvm.helloworld2:8648]      compile:   6,008.76 ms,  2.98 GB
[test.aaa.graalvm.helloworld2:8648]        image:     736.79 ms,  2.98 GB
[test.aaa.graalvm.helloworld2:8648]        write:     192.36 ms,  2.98 GB
[test.aaa.graalvm.helloworld2:8648]      [total]:  18,209.02 ms,  2.98 GB
# Printing build artifacts to: D:\project\idea\test_graalvm\target\test.aaa.graalvm.helloworld2.build_artifacts.txt
Warning: Image 'test.aaa.graalvm.helloworld2' is a fallback image that requires a JDK for execution (use --no-fallback to suppress fallback image generation and to print more detailed information why a fallback image was necessary).

雙擊或命令行下輸入test.aaa.graalvm.helloworld2.exe運行

 

測試項目代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章