GraalVM 将 Java 程序编译成可执行文件,解决反射的问题(maven 工程)

提出问题

GraalVM 将 Java 程序编译成可执行文件,反射功能无法使用

使用 log4j 后,可以正常编译,但执行报错

maven 工程 使用log4j

pom.xml

<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>

java

package webserver;

import org.apache.log4j.Logger;

public class Log4jStd {
    static final Logger LOGGER= Logger.getLogger(Test.class);

    public static void main(String[] args) {
        LOGGER.info("Hello world");
    }
}

分析

Graalvm通过静态分析提前编译来为Java应用程序构建高度优化的本机可执行文件,需要在 编译时就知道所有的程序类型,而java中的反射、动态代理等功能,在编译时不确定具体的类型,所以在使用GraalVm构建native image前需要通过配置列出反射可见的所有类型。

解决

graavm提供 agent工具,该工具会生成整个代码中需要用到反射的配置文件

实现方式一

  1. 将项目打成jar包
  2. 根据 jar 包,生成需要用到反射的配置文件
  3. 编译成可执行文件

将项目打成jar包

plugin插件

pom.xml 中增加下面 plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.4</version>
    <executions>
        <execution>
            <id>package-jar</id>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <!-- 主启动类 -->
                        <mainClass>webserver.Log4jStd</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

生成 jar

在 idea 上点击 packeage 生成jar包

结果如下:

生成需要用到反射的配置文件

执行下面命令

java -agentlib:native-image-agent=config-output-dir=D:\java\graalvm_std\src\main\resources\META-INF\native-image  -jar  D:\java\graalvm_std\target\graalvm_std-1.0-SNAPSHOT.jar

解释:

java -agentlib:native-image-agent=config-output-dir=resources目录\META-INF\native-image  -jar  jar文件名
  • resources目录\META-INF\native-image:maven 工程,配置文件必须在 resources
  • target目录\.jar文件名:上面生成 jar文件路径

执行结果1

从控制台可以看出,相当于执行 java -jar xxx.jar,也就是正常执行 .jar 程序

注意(关键)

一定要确保正常执行 .jar 程序,不能因为缺少配置文件、启动参数不正确等原因,导致报错、终止运行,这种情况导致下一步生成的 二进制可执行文件 不能正常运行

执行结果2

生成配置文件: reflect-config.json,内容如下:

[
{
  "name":"java.beans.PropertyVetoException"
},
{
  "name":"java.lang.Object",
  "queryAllPublicMethods":true
},
{
  "name":"java.lang.String"
},
{
  "name":"java.lang.Thread",
  "methods":[{"name":"getContextClassLoader","parameterTypes":[] }]
},
{
  "name":"org.apache.log4j.Appender"
},
{
  "name":"org.apache.log4j.Category"
},
{
  "name":"org.apache.log4j.ConsoleAppender",
  "methods":[{"name":"<init>","parameterTypes":[] }]
},
{
  "name":"org.apache.log4j.Layout",
  "queryAllPublicMethods":true
},
{
  "name":"org.apache.log4j.Logger"
},
{
  "name":"org.apache.log4j.PatternLayout",
  "queryAllPublicMethods":true,
  "methods":[
    {"name":"<init>","parameterTypes":[] }, 
    {"name":"setConversionPattern","parameterTypes":["java.lang.String"] }
  ]
},
{
  "name":"org.apache.log4j.helpers.Loader"
},
{
  "name":"org.apache.log4j.spi.OptionHandler"
}
]

编译成可执行文件-方式一

plugin插件

注释掉上面的plugin,加上下面的plugin:

<plugin>
    <groupId>org.graalvm.nativeimage</groupId>
    <artifactId>native-image-maven-plugin</artifactId>
    <version>21.2.0</version>
    <executions>
        <execution>
            <goals>
                <goal>native-image</goal>
            </goals>
            <phase>package</phase>
        </execution>
    </executions>
    <configuration>
        <!-- imageName用于设置生成的二进制文件名称 -->
        <imageName>test</imageName>
        <!-- mainClass用于指定main方法类路径 -->
        <mainClass>webserver.Log4jStd</mainClass>
        <!-- native image 编译参数文档:https://docs.oracle.com/en/graalvm/enterprise/20/docs/reference-manual/native-image/NativeImageMavenPlugin/ -->
        <buildArgs>
            <!-- 构建独立镜像或报告故障 -->
            --no-fallback

        </buildArgs>
    </configuration>
</plugin>

参数解释:https://www.graalvm.org/22.0/reference-manual/native-image/Options/

命令

在工程根目录下执行下面命令:

mvn -Pnative clean package

生成文件如下:

除了可执行文件,还有其他文件,亲测,有时不需要其他文件,仅可执行文件,就可以执行

编译成可执行文件-方式二

plugin插件

注释掉上面的plugin,加上下面的plugin:

<plugin>
    <groupId>org.graalvm.nativeimage</groupId>
    <artifactId>native-image-maven-plugin</artifactId>
    <version>21.2.0</version>
    <executions>
        <execution>
            <goals>
                <goal>native-image</goal>
            </goals>
            <phase>package</phase>
        </execution>
    </executions>
    <configuration>
        <!-- imageName用于设置生成的二进制文件名称 -->
        <imageName>test</imageName>
        <!-- mainClass用于指定main方法类路径 -->
        <mainClass>webserver.Log4jStd</mainClass>
        <!-- native image 编译参数文档:https://docs.oracle.com/en/graalvm/enterprise/20/docs/reference-manual/native-image/NativeImageMavenPlugin/ -->
        <buildArgs>
            <!-- 构建独立镜像或报告故障 -->
            --no-fallback

        </buildArgs>
    </configuration>
</plugin>

参数解释:https://www.graalvm.org/22.0/reference-manual/native-image/Options/

package

通过 idea 的 maven 执行 package 命令,即可生成(但可能有问题)

方式二

依赖

在 pom.xml 中的 <buildArgs> 配置 --initialize-at-build-time,表示 在映像构建期间初始化的包和类

如何知道有哪些配置,目前网上没有资料,解决方法是:
在 idea 上点击 packeage 生成可执行文件时,会报错,将报错的类添加进去

<build>
    <plugins>

        <plugin>
            <groupId>org.graalvm.nativeimage</groupId>
            <artifactId>native-image-maven-plugin</artifactId>
            <version>21.2.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>native-image</goal>
                    </goals>
                    <phase>package</phase>
                </execution>
            </executions>
            <configuration>
                <!-- imageName用于设置生成的二进制文件名称 -->
                <imageName>test</imageName>
                <!-- mainClass用于指定main方法类路径 -->
                <mainClass>webserver.Log4jStd</mainClass>
                <!-- native image 编译参数文档:https://docs.oracle.com/en/graalvm/enterprise/20/docs/reference-manual/native-image/NativeImageMavenPlugin/ -->
                <buildArgs>
                    <!-- 构建独立镜像或报告故障 -->
                    --no-fallback
                    <!-- 在映像构建期间初始化的包和类 -->
                    --initialize-at-build-time=org.apache.log4j.PatternLayout
                    --initialize-at-build-time=org.apache.log4j.Logger
                    --initialize-at-build-time=org.apache.log4j.helpers.LogLog
                    --initialize-at-build-time=org.apache.log4j.LogManager
                    --initialize-at-build-time=org.apache.log4j.helpers.Loader
                    -H:+ReportExceptionStackTraces
                </buildArgs>
            </configuration>
        </plugin>

    </plugins>
</build>

生成

在 idea 上点击 packeage 生成可执行文件时

参考:
https://www.cnblogs.com/cfas/p/16339789.html
https://blog.csdn.net/weixin_44123053/article/details/120486756


原文出处:https://malaoshi.top/show_1IX4q2jwtNjd.html