
Проблема
Долгое время программировал на java и spring boot 1.5. Но возникла потребность написать новый проект:
- Имею json для интеграции 1600 строк, некоторые классы имеют 100 полей
- Захотелось опробовать Kotlin и Quarkus.
- Написать rest controller, который бы умел работать с kotlin data class без аннотаций и без привлечения магии lombok. Хочется, чтобы data class был небольшого размера
Вы наверное догадались, что kotlin data class — это неизменяемый класс, или immutable. Класс у которого конструктор содержит все поля. Я большой приверженец такой концепции; после создания класса, его нельзя изменить, в нем нет сеттеров. Как в мире докер image не может изменен, так и дата класс, который попал в контроллер, это то, чего нельзя менять.
Давайте рассмотрим возможные пути решения проблемы, точнее как в современных проектах можно написать контроллер:
Стандартный вариант. вручную на spring или spring boot 1.5
Rest контроллеры появились достаточно давно, и типичным примером их использования на java, который есть во всех туториалах является следующий пример.
Просто создаем простейший контроллер
@RestController public class FruitController { @PostMapping("/fruit") public void greeting(@RequestBody Fruit request) { System.out.println(request); } }
И создаем POJO(plain old java object)
public class Fruit { public String name; public String description; public Fruit() { } public Fruit(String name, String description) { this.name = name; this.description = description; } }
тут могут быть вариации с private полями и getters/setters или lombok аннотациями, но не суть.
Замечательно, мы создали первый рест контроллер он работает. Для 90% случаев рабочий вариант. Можно здесь и остановиться.
Проблемы:
Нарушена концепция immutable.
Немного многословный класс данных.
Можем ли мы сделать immutable?
Естественной реакцией на эту проблему будет решение удалить конструктор по умолчанию и запретить редактировать поля класса.
@Getter public class Fruit { private String name; private String description; // public Fruit() { // } public Fruit(String name, String description) { this.name = name; this.description = description; } }
Но теперь возникает проблема, оказывается, что библиотека (скорее всего jackson) не может создать класс. Наиболее вероятно вы увидим ошибку вроде No default constructor found.
Значит jackson сначала создавал класс с помощью конструктора без параметров, а потом вызывал getter/setter. Какой ужас. Ведь есть же конструктор со всеми параметрами? Но к сожалению, когда класс откомпилирован, параметры выглядят примерно так.

Т.е. во время выполнения java ничего не знает об именах параметров в конструкторе. Компилятор их теряет.
Второй вариант spring или spring boot 1.5 + аннотации как спасенье
Итак, мы осознали, что хотим immutable class, и знаем что пользуемся jackson. Тогда на помощь приходят анотатации.
@Getter public class Fruit { private String name; private String description; @JsonCreator public Fruit(@JsonProperty("name") String name, @JsonProperty("description")String description) { this.name = name; this.description = description; } }
В данный момент в нашем проекте на sping boot 1.5 этими аннотациями буквально пестрит все.
А если вы возьмете популярный генератор jsonschema2pojo, то он сгенерирует еще больше аннотаций. Честно говоря, мне они не нравятся.
Попробуйте скопировать туда:
{ "description": "description", "name": "name" }
На выходе получаем(можно зажмуриться и пролистать):
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "description", "name" }) public class Example { @JsonProperty("description") private String description; @JsonProperty("name") private String name; @JsonProperty("description") public String getDescription() { return description; } @JsonProperty("description") public void setDescription(String description) { this.description = description; } @JsonProperty("name") public String getName() { return name; } @JsonProperty("name") public void setName(String name) { this.name = name; } }
Минусы: С аннотациями сильно раздувается класс. Очень многословно, на любителя.
Третий вариант spring или spring boot 1.5 + флаг lombok
Спасибо Throwable я процитирую из комментария:
«Если Вы используете Lombok, то есть лучший способ — прописать в lombok.config:
lombok.allArgsConstructor.addConstructorProperties = true
Это сгенерит на конструкторе @java.beans.ConstructorProperties, который Jackson умеет понимать.»
Замечательный вариант. Он наверняка бы спас меня от многословности аннотаций.
Минусы:
Но я хочу использовать Kotlin без lombok.
Я узнал этот вариант слишком поздно.
Четвертый вариант Spring boot 2.0
@Getter public class Fruit { private String name; private String description; public Fruit( String name, String description) { this.name = name; this.description = description; } }
На удивление spring boot 2.0 спокойно работает с таким immutable классом. А также с его братом близнецом kotlin data class
data class Fruit( val name : String, val description : String)
Казалось бы java в рантайме не знает имен параметров в конструкторе, но почему то spring boot2 уже умеет работать с data class. Итак, заглянем в spring-boot-starter-parent, там добавилась поддержка Kotlin.
<plugin> <groupId>org.jetbrains.kotlin</groupId> ... <configuration> <javaParameters>true</javaParameters> </configuration> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <parameters>true</parameters> </configuration> </plugin>
Расшифровываю. Для того чтобы имена параметров в конструкторе класса не терялись при рантайме компилятору необходимо передать флаг javac -parameters И spring boot 2.0 это и делает.
Пример проекта на spring boot 2.
Пятый вариант. Quarkus + Kotlin
У кваркуса есть пример rest service-а, аналог моего первого варианта. Т.е. rest контроллер по старинке. Однако, если вы хотите использовать его с Kotlin вам придется добавить флаги как это сделал spring boot 2.
Описание проблемы тут. Пример, как добавить в кваркус поддержку kotlin data class тут.
Выводы
Можно пользоваться первым простейшим вариантом создания rest контроллеров, но я бы посоветовал бы двигаться в сторону immutable классов. Пишите на Kotlin и вам не понадобится lombok. Код должен стать легче и проще. Я уверен, что создатели spring осознано шли на добавление javac -parameters в опции компилятора и в этом не должно быть криминала. Всем удачи на пути к идеальному коду.
Всем спасибо за внимание!
ссылка на оригинал статьи https://habr.com/ru/post/480158/
Добавить комментарий