1

我用 Micronaut 2.0.0 创建了一个简单的应用程序:它是使用 Micronaut 应用程序生成器在https://micronaut.io/launch/生成的,然后我添加了以下内容:

@Controller
public class HelloController {
    @Get("/hello")
    public Result hello() {
        return new Result("Hello world!");
    }
}

public class Result {
    private final String message;

    public Result(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

然后我使用./mvnw clean package.

当我用 启动它时java -jar target/micronaut-minimal-1.0.jar,它成功启动。它正确响应:

$ curl http://localhost:8080/hello
{"message":"Hello world!"}

现在我构建一个原生镜像:

native-image -jar target/micronaut-minimal-0.1.jar target/app

然后我运行它:

target/app

它开始正常。

现在同样的请求产生了一个错误:

$ curl http://localhost:8080/hello
{"message":"Internal Server Error: Error encoding object [com.example.Result@7f21a9858b70] to JSON: No serializer found for class com.example.Result and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)"}

我尝试添加@JsonPropertymessage字段或getMessage()方法(带或不带value属性),但无济于事。

我认为这与反射可能在本机图像中不可用的事实有关。但是我该如何解决这个问题呢?

我正在使用 GraalVM CE 20.1.0(构建 11.0.7+10-jvmci-20.1-b02)来运行和构建 jar 和图像。

4

2 回答 2

2

我找到了两种解决问题的方法:一种具体且更干净,另一种更通用但更脏。

@Introspected 注解

更简洁的方法非常简单:只需使用以下内容注释类@Introspected

@Introspected
public class Result {
    private final String message;

    public Result(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

而已。如果我理解正确,这个注释使 Micronaut 在编译时生成自省类,然后可能由 Jackson 序列化机器使用。文件说

您可以使用 jackson.bean-introspection-module 设置启用与 Jackson 的 bean 自省集成,以实现无反射 JSON 序列化和反序列化。

但就我而言,它只是奏效了。

反射配置

正如@Turing85 所指出的那样,Result#message代码Result#getMessage()中没有引用它们,native-image不知道我们在运行时仍然需要它们,因此反射 API 没有留下任何痕迹(Jackson 默认可能使用它)。

我们可以指示native-image工具保留这些信息。首先,我们需要创建一个 JSON 配置(在我的例子中,文件名为reflect-config.json):

[
  {
    "name" : "com.example.Result",
    "fields" : [
      { "name" : "message" }
    ],
    "methods" : [
      { "name" : "getMessage", "parameterTypes" : [] }
    ]
  }
]

这里我们指示工具保留字段message和方法getMessage()

然后我们使用-H:ReflectionConfigurationFiles

native-image --no-server -H:ReflectionConfigurationFiles=reflect-config.json -jar target/micronaut-minimal-0.1.jar target/app

更多信息:https ://github.com/oracle/graal/blob/master/substratevm/REFLECTION.md

方法比较

第一种方法更简洁,因为它完全避免了反射的使用(这意味着代码运行得更快并且消耗更少的内存)。

第二种方法仍然更通用,因为第一种方法只有在特别处理的情况下才有可能。如果一个工具仍然需要反思,你别无选择,只能坚持下去。

于 2020-07-05T16:16:48.727 回答
0

只需将 @JsonSerialize 添加到您的 pojo 类中即可。

@JsonSerialize
public class Result {
    private final String message;

    public Result(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
于 2021-05-11T05:54:13.640 回答