前文介绍了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运行