Джексон не находит свойство POJO в приложении Micronaut, скомпилированном в собственный образ

Я создал простое приложение с 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)"}

Я попытался добавить @JsonProperty в поле message или метод getMessage() (с атрибутом value или без него), но безрезультатно.

Я считаю, что это как-то связано с тем фактом, что отражение, вероятно, недоступно в собственном образе. Но как мне решить эту проблему?

Я использую GraalVM CE 20.1.0 (сборка 11.0.7 + 10-jvmci-20.1-b02) как для запуска, так и для сборки jar-файла и образа.


person Roman Puchkovskiy    schedule 04.07.2020    source источник
comment
Компилятор собственных образов GraalVM выполняет значительную оптимизацию во время компиляции. Помимо прочего, он может удалять неиспользуемые поля и / или методы. Однако компилятор не видит всех рефлексивных обращений, поэтому он может удалять поля или методы, которые вызываются рефлексивно. Чтобы этого не произошло, можно указать компилятору не оптимизировать (определенные части) определенные классы. Дополнительные сведения см. На этой странице (раздел «Ручная настройка»).   -  person Turing85    schedule 04.07.2020


Ответы (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 генерировать классы самоанализа во время компиляции, которые затем, вероятно, используются механизмом сериализации Джексона. В документации сказано, что

Вы можете включить интеграцию интроспекции bean-компонентов с Jackson для сериализации и десериализации JSON без отражения, используя параметр jackson.bean-introspection-module.

но в моем случае это просто сработало.

Конфигурация отражения

Как отмечает @ Turing85, поскольку Result#message и Result#getMessage() не упоминаются в коде, native-image не знает, что они нам все еще нужны во время выполнения, поэтому для Reflection API не остается их следов (который, вероятно, используется Джексоном по умолчанию).

Мы можем поручить инструменту 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

Сравнение подходов

Первый подход чище, потому что он полностью исключает использование отражения (что означает, что код работает быстрее и потребляет меньше памяти).

Второй подход еще более универсален, потому что первый возможен только при особом обращении. Если инструмент по-прежнему требует отражения, у вас нет другого выбора, кроме как придерживаться его.

person Roman Puchkovskiy    schedule 05.07.2020
comment
с Micronaut вы также можете использовать аннотацию @TypeHint - person Airy; 05.07.2020

Просто добавьте @JsonSerialize в свой класс pojo.

@JsonSerialize
public class Result {
    private final String message;

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

    public String getMessage() {
        return message;
    }
}
person Shïvà Tömàr    schedule 11.05.2021