Умеем ли мы готовить Java, Kotlin RestController?

от автора

Практически любой программист на java в своей жизни писал RestController, но мало кто задумывается правильно ли он это делает. Даже если вы опытный программист, у вас могут возникнуть вопросы, на которые я постараюсь ответить. В статье будут затронуты такие фреймворки как spring boot версии 1.5 и 2.0, а также quarkus — недавно появившийся соперник spring boot от red hat.

image

Проблема

Долгое время программировал на java и spring boot 1.5. Но возникла потребность написать новый проект:

  1. Имею json для интеграции 1600 строк, некоторые классы имеют 100 полей
  2. Захотелось опробовать Kotlin и Quarkus.
  3. Написать 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. Какой ужас. Ведь есть же конструктор со всеми параметрами? Но к сожалению, когда класс откомпилирован, параметры выглядят примерно так.

image

Т.е. во время выполнения 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *