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

Долгое время программировал на java и spring boot 1.5. Но возникла потребность написать новый проект:
Вы наверное догадались, что kotlin data class — это неизменяемый класс, или immutable. Класс у которого конструктор содержит все поля. Я большой приверженец такой концепции; после создания класса, его нельзя изменить, в нем нет сеттеров. Как в мире докер image не может изменен, так и дата класс, который попал в контроллер, это то, чего нельзя менять.
Давайте рассмотрим возможные пути решения проблемы, точнее как в современных проектах можно написать контроллер:
Rest контроллеры появились достаточно давно, и типичным примером их использования на java, который есть во всех туториалах является следующий пример.
Просто создаем простейший контроллер
И создаем POJO(plain old java object)
тут могут быть вариации с private полями и getters/setters или lombok аннотациями, но не суть.
Замечательно, мы создали первый рест контроллер он работает. Для 90% случаев рабочий вариант. Можно здесь и остановиться.
Проблемы:
Нарушена концепция immutable.
Немного многословный класс данных.
Естественной реакцией на эту проблему будет решение удалить конструктор по умолчанию и запретить редактировать поля класса.
Но теперь возникает проблема, оказывается, что библиотека (скорее всего jackson) не может создать класс. Наиболее вероятно вы увидим ошибку вроде No default constructor found.
Значит jackson сначала создавал класс с помощью конструктора без параметров, а потом вызывал getter/setter. Какой ужас. Ведь есть же конструктор со всеми параметрами? Но к сожалению, когда класс откомпилирован, параметры выглядят примерно так.

Т.е. во время выполнения java ничего не знает об именах параметров в конструкторе. Компилятор их теряет.
Итак, мы осознали, что хотим immutable class, и знаем что пользуемся jackson. Тогда на помощь приходят анотатации.
В данный момент в нашем проекте на sping boot 1.5 этими аннотациями буквально пестрит все.
А если вы возьмете популярный генератор jsonschema2pojo, то он сгенерирует еще больше аннотаций. Честно говоря, мне они не нравятся.
Попробуйте скопировать туда:
На выходе получаем(можно зажмуриться и пролистать):
Минусы: С аннотациями сильно раздувается класс. Очень многословно, на любителя.
Спасибо Throwable я процитирую из комментария:
«Если Вы используете Lombok, то есть лучший способ — прописать в lombok.config:
Это сгенерит на конструкторе
Замечательный вариант. Он наверняка бы спас меня от многословности аннотаций.
Минусы:
Но я хочу использовать Kotlin без lombok.
Я узнал этот вариант слишком поздно.
На удивление spring boot 2.0 спокойно работает с таким immutable классом. А также с его братом близнецом kotlin data class
Казалось бы java в рантайме не знает имен параметров в конструкторе, но почему то spring boot2 уже умеет работать с data class. Итак, заглянем в spring-boot-starter-parent, там добавилась поддержка Kotlin.
Расшифровываю. Для того чтобы имена параметров в конструкторе класса не терялись при рантайме компилятору необходимо передать флаг javac -parameters И spring boot 2.0 это и делает.
Пример проекта на spring boot 2.
У кваркуса есть пример rest service-а, аналог моего первого варианта. Т.е. rest контроллер по старинке. Однако, если вы хотите использовать его с Kotlin вам придется добавить флаги как это сделал spring boot 2.
Описание проблемы тут. Пример, как добавить в кваркус поддержку kotlin data class тут.
Можно пользоваться первым простейшим вариантом создания rest контроллеров, но я бы посоветовал бы двигаться в сторону immutable классов. Пишите на Kotlin и вам не понадобится lombok. Код должен стать легче и проще. Я уверен, что создатели spring осознано шли на добавление javac -parameters в опции компилятора и в этом не должно быть криминала. Всем удачи на пути к идеальному коду.
Всем спасибо за внимание!

Проблема
Долгое время программировал на 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 в опции компилятора и в этом не должно быть криминала. Всем удачи на пути к идеальному коду.
Всем спасибо за внимание!