Spring Boot — это продуманная среда, которая упрощает разработку приложений Spring. Это освобождает нас от рабства сложных файлов конфигурации и помогает нам создавать автономные приложения Spring, которым не нужен внешний контейнер сервлета.
Это звучит слишком хорошо, чтобы быть правдой, но Spring Boot действительно может все это сделать .
Эта запись блога демонстрирует, как легко реализовать API REST, который обеспечивает операции CRUD для записей todo, которые сохраняются в базе данных MongoDB .
Давайте начнем с создания нашего проекта Maven.
Примечание. В этом сообщении предполагается, что вы уже установили базу данных MongoDB. Если вы этого не сделали, вы можете следовать инструкциям, приведенным в сообщении в блоге: Доступ к данным с помощью MongoDB .
Создание нашего проекта Maven
Мы можем создать наш проект Maven, выполнив следующие действия:
- Используйте POM spring-boot-starter-parent в качестве родительского POM нашего проекта Maven. Это гарантирует, что наш проект наследует разумные настройки по умолчанию от Spring Boot.
- Добавьте плагин Spring Boot Maven в наш проект. Этот плагин позволяет нам упаковать наше приложение в исполняемый файл JAR, упаковать его в военный архив и запустить приложение.
- Настройте зависимости нашего проекта. Нам нужно настроить следующие зависимости:
- Зависимость spring-boot-starter-web предоставляет зависимости веб-приложения.
- Зависимость spring-data-mongodb обеспечивает интеграцию с базой данных документов MongoDB.
- Включить поддержку Java 8 Spring Boot.
- Настройте основной класс нашего приложения. Этот класс отвечает за настройку и запуск нашего приложения.
Соответствующая часть нашего файла pom.xml выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<properties> <!-- Enable Java 8 --> <java.version> 1.8 </java.version> <project.build.sourceEncoding>UTF- 8 </project.build.sourceEncoding> <!-- Configure the main class of our Spring Boot application --> <start- class >com.javaadvent.bootrest.TodoAppConfig</start- class > </properties> <!-- Inherit defaults from Spring Boot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version> 1.1 . 9 .RELEASE</version> </parent> <dependencies> <!-- Get the dependencies of a web application --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Data MongoDB--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> </dependency> </dependencies> <build> <plugins> <!-- Spring Boot Maven Support --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> |
Дополнительное чтение:
- Spring Boot Справочное руководство: 9.1.1 Установка Maven
- Spring Boot Справочное руководство: 12.1 Maven
- Плагин Spring Boot Maven — Использование
Давайте продолжим и узнаем, как мы можем настроить наше приложение.
Настройка нашего приложения
Мы можем настроить наше приложение Spring Boot, выполнив следующие действия:
- Создайте класс TodoAppConfig для пакета com.javaadvent.bootrest .
- Включить автозагрузку Spring Boot.
- Сконфигурируйте контейнер Spring для сканирования компонентов, найденных в дочерних пакетах пакета com.javaadvent.bootrest .
- Добавьте метод main () в класс TodoAppConfig и реализуйте его, запустив наше приложение.
Исходный код класса TodoAppConfig выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
package com.javaadvent.bootrest; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @EnableAutoConfiguration @ComponentScan public class TodoAppConfig { public static void main(String[] args) { SpringApplication.run(TodoAppConfig. class , args); } } |
Теперь мы создали класс конфигурации, который настраивает и запускает наше приложение Spring Boot. Поскольку файлы MongoDB находятся в пути к классам, Spring Boot настраивает подключение MongoDB с использованием настроек по умолчанию.
Дополнительное чтение:
- Spring Boot Справочное руководство: 13.2 Расположение основного класса приложения
- Spring Boot Справочное руководство: 14. Классы конфигурации
- Javadoc аннотации @EnableAutoConfiguration
- Spring Boot Справочное руководство: 15. Автоконфигурация
- Javadoc класса SpringApplication
- Справочное руководство Spring Boot: 27.2.1 Подключение к базе данных MongoDB
Давайте перейдем к реализации нашего REST API.
Реализация нашего REST API
Нам нужно реализовать REST API, который обеспечивает операции CRUD для записей todo. Требования нашего REST API:
- Запрос POST, отправляемый на URL ‘/ api / todo’, должен создать новую запись todo, используя информацию, найденную в теле запроса, и вернуть информацию о созданной записи todo.
- Запрос DELETE, отправленный на URL ‘/ api / todo / {id}’, должен удалить запись todo, идентификатор которой найден в URL, и вернуть информацию об удаленной записи todo.
- Запрос GET, отправленный на URL ‘/ api / todo’, должен вернуть все записи todo, найденные в базе данных.
- Запрос GET, отправляемый на URL ‘/ api / todo / {id}’, должен возвращать информацию о записи todo, идентификатор которой найден из URL.
- Запрос PUT, отправляемый на URL ‘/ api / todo / {id}’, должен обновить информацию о существующей записи todo, используя информацию, найденную в теле запроса, и вернуть информацию об обновленной записи todo.
Мы можем выполнить эти требования, выполнив следующие действия:
- Создайте сущность, которая содержит информацию об одной записи todo.
- Создайте репозиторий, который используется для сохранения записей задач в базе данных MongoDB, и найдите в них записи задач.
- Создайте сервисный уровень, который отвечает за отображение DTO в доменные объекты и наоборот. Цель нашего сервисного уровня — изолировать нашу модель домена от веб-слоя.
- Создайте класс контроллера, который обрабатывает HTTP-запросы и возвращает правильный ответ клиенту.
Примечание. Этот пример настолько прост, что мы можем просто вставить наш репозиторий в наш контроллер. Однако, поскольку мы не реализуем стратегию при реализации реальных приложений, мы добавим сервисный уровень между веб-уровнями и уровнями репозитория.
Давайте начнем.
Создание сущности
Нам нужно создать класс сущностей, который содержит информацию об одной записи todo. Мы можем сделать это, выполнив следующие действия:
- Добавьте поля id , description и title в созданный класс сущностей. Сконфигурируйте поле id объекта, добавив в поле id аннотацию @Id .
- Укажите константы ( MAX_LENGTH_DESCRIPTION и MAX_LENGTH_TITLE ), которые определяют максимальную длину полей описания и заголовка .
- Добавьте класс статического построителя в класс сущности. Этот класс используется для создания новых объектов Todo .
- Добавьте метод update () в класс сущностей. Этот метод просто обновляет заголовок и описание объекта, если в качестве параметров метода указаны действительные значения.
Исходный код класса Todo выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
import org.springframework.data.annotation.Id; import static com.javaadvent.bootrest.util.PreCondition.isTrue; import static com.javaadvent.bootrest.util.PreCondition.notEmpty; import static com.javaadvent.bootrest.util.PreCondition.notNull; final class Todo { static final int MAX_LENGTH_DESCRIPTION = 500 ; static final int MAX_LENGTH_TITLE = 100 ; @Id private String id; private String description; private String title; public Todo() {} private Todo(Builder builder) { this .description = builder.description; this .title = builder.title; } static Builder getBuilder() { return new Builder(); } //Other getters are omitted public void update(String title, String description) { checkTitleAndDescription(title, description); this .title = title; this .description = description; } /** * We don't have to use the builder pattern here because the constructed * class has only two String fields. However, I use the builder pattern * in this example because it makes the code a bit easier to read. */ static class Builder { private String description; private String title; private Builder() {} Builder description(String description) { this .description = description; return this ; } Builder title(String title) { this .title = title; return this ; } Todo build() { Todo build = new Todo( this ); build.checkTitleAndDescription(build.getTitle(), build.getDescription()); return build; } } private void checkTitleAndDescription(String title, String description) { notNull(title, "Title cannot be null" ); notEmpty(title, "Title cannot be empty" ); isTrue(title.length() <= MAX_LENGTH_TITLE, "Title cannot be longer than %d characters" , MAX_LENGTH_TITLE ); if (description != null ) { isTrue(description.length() <= MAX_LENGTH_DESCRIPTION, "Description cannot be longer than %d characters" , MAX_LENGTH_DESCRIPTION ); } } } |
Дополнительное чтение:
Давайте продолжим и создадим репозиторий, который связывается с базой данных MongoDB.
Создание хранилища
Мы должны создать интерфейс репозитория, который используется для сохранения объектов Todo в базе данных MondoDB и извлечения объектов Todo из нее.
Если мы не хотим использовать поддержку Spring 8 для Spring Data, мы могли бы создать наш репозиторий, создав интерфейс, расширяющий интерфейс CrudeRepository <T, ID> . Однако, поскольку мы хотим использовать поддержку Java 8, мы должны выполнить следующие шаги:
- Создайте интерфейс, расширяющий интерфейс хранилища <T, ID> .
- Добавьте следующие методы хранилища в созданный интерфейс:
- Метод void delete (удаленный Todo) удаляет запись todo, заданную в качестве параметра метода.
- Метод List <Todo> findAll () возвращает все записи задач, найденные в базе данных.
- Необязательный метод <Todo> findOne (String id) возвращает информацию об одной записи todo. Если запись todo не найдена, этот метод возвращает пустое значение Optional .
- Метод сохранения Todo (сохранение Todo) сохраняет новую запись задачи в базу данных и возвращает сохраненную запись задачи.
Исходный код интерфейса TodoRepository выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
import org.springframework.data.repository.Repository; import java.util.List; import java.util.Optional; interface TodoRepository extends Repository<Todo, String> { void delete(Todo deleted); List<Todo> findAll(); Optional<Todo> findOne(String id); Todo save(Todo saved); } |
Дополнительное чтение:
- Javadoc интерфейса CrudeRepository <T, ID>
- Интерфейс Javadoc репозитория <T, ID>
- Справочное руководство по Spring Data MongoDB: 5. Работа с репозиториями Spring Data
- Справочное руководство по Spring Data MongoDB: 5.3.1 Точная настройка определения репозитория
Давайте продолжим и создадим сервисный уровень нашего примера приложения.
Создание сервисного уровня
Во-первых , мы должны создать сервисный интерфейс, который обеспечивает операции CRUD для записей todo. Исходный код интерфейса TodoService выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
import java.util.List; interface TodoService { TodoDTO create(TodoDTO todo); TodoDTO delete(String id); List<TodoDTO> findAll(); TodoDTO findById(String id); TodoDTO update(TodoDTO todo); } |
Класс TodoDTO — это DTO, который содержит информацию об одной записи todo. Мы поговорим об этом больше, когда создадим веб-слой нашего примера приложения.
Во-вторых , мы должны реализовать интерфейс TodoService. Мы можем сделать это, выполнив следующие действия:
- Внедрите наш репозиторий в класс обслуживания, используя внедрение конструктора.
- Добавьте частный метод Todo findTodoById (String id) в класс обслуживания и реализуйте его, возвращая найденный объект Todo или выбрасывая исключение TodoNotFoundException .
- Добавьте частный метод TodoDTO convertToDTO (модель Todo) в класс обслуживания и реализуйте его, преобразовав объект Todo в объект TodoDTO и вернув созданный объект.
- Добавьте закрытый List <TodoDTO> convertToDTOs (List <Todo> models) и реализуйте его, преобразовав список объектов Todo в список объектов TodoDTO и вернув созданный список.
- Реализуйте метод создания TodoDTO (TodoDTO todo) . Этот метод создает новый объект Todo , сохраняет созданный объект в базе данных MongoDB и возвращает информацию о созданной записи todo.
- Реализуйте метод удаления (String id) TodoDTO . Этот метод находит удаленный объект Todo , удаляет его и возвращает информацию об удаленной записи todo. Если объект Todo с данным идентификатором не найден, этот метод генерирует исключение TodoNotFoundException .
- Реализуйте метод List <TodoDTO> findAll () . Этот метод извлекает все объекты Todo из базы данных, преобразует их в список объектов TodoDTO и возвращает созданный список.
- Реализуйте метод findById (String id) TodoDTO . Этот метод находит объект Todo из базы данных, преобразует его в объект TodoDTO и возвращает созданный объект TodoDTO . Если запись todo не найдена, этот метод генерирует исключение TodoNotFoundException .
- Реализуйте метод обновления TodoDTO (TodoDTO todo) . Этот метод находит обновленный объект Todo из базы данных, обновляет его заголовок и описание , сохраняет его и возвращает обновленную информацию. Если обновленный объект Todo не найден, этот метод генерирует исключение TodoNotFoundException .
Исходный код MongoDBTodoService выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; import static java.util.stream.Collectors.toList; @Service final class MongoDBTodoService implements TodoService { private final TodoRepository repository; @Autowired MongoDBTodoService(TodoRepository repository) { this .repository = repository; } @Override public TodoDTO create(TodoDTO todo) { Todo persisted = Todo.getBuilder() .title(todo.getTitle()) .description(todo.getDescription()) .build(); persisted = repository.save(persisted); return convertToDTO(persisted); } @Override public TodoDTO delete(String id) { Todo deleted = findTodoById(id); repository.delete(deleted); return convertToDTO(deleted); } @Override public List findAll() { List todoEntries = repository.findAll(); return convertToDTOs(todoEntries); } private List convertToDTOs(List models) { return models.stream() .map( this ::convertToDTO) .collect(toList()); } @Override public TodoDTO findById(String id) { Todo found = findTodoById(id); return convertToDTO(found); } @Override public TodoDTO update(TodoDTO todo) { Todo updated = findTodoById(todo.getId()); updated.update(todo.getTitle(), todo.getDescription()); updated = repository.save(updated); return convertToDTO(updated); } private Todo findTodoById(String id) { Optional result = repository.findOne(id); return result.orElseThrow(() -> new TodoNotFoundException(id)); } private TodoDTO convertToDTO(Todo model) { TodoDTO dto = new TodoDTO(); dto.setId(model.getId()); dto.setTitle(model.getTitle()); dto.setDescription(model.getDescription()); return dto; } } |
Теперь мы создали сервисный уровень нашего примера приложения. Давайте двигаться дальше и создадим класс контроллера.
Создание класса контроллера
Во-первых , нам нужно создать класс DTO, который содержит информацию об одной записи todo и определяет правила проверки, которые используются, чтобы гарантировать, что в базу данных может быть сохранена только действительная информация. Исходный код класса TodoDTO выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.Size; public final class TodoDTO { private String id; @Size (max = Todo.MAX_LENGTH_DESCRIPTION) private String description; @NotEmpty @Size (max = Todo.MAX_LENGTH_TITLE) private String title; //Constructor, getters, and setters are omitted } |
Дополнительное чтение:
Во-вторых , нам нужно создать класс контроллера, который обрабатывает запросы HTTP, отправленные нашему REST API, и отправляет правильный ответ клиенту. Мы можем сделать это, выполнив следующие действия:
- Внедрите наш сервис в наш контроллер с помощью инжектора конструктора.
- Добавьте метод create () в наш контроллер и реализуйте его, выполнив следующие действия:
- Прочитайте информацию о созданной записи todo из тела запроса.
- Проверьте информацию о созданной записи todo.
- Создайте новую запись todo и верните созданную запись todo. Установите статус ответа на 201.
- Внедрите метод delete () , делегировав идентификатор удаленной записи задачи в нашу службу и вернув удаленную запись задачи.
- Реализуйте метод findAll () , найдя записи todo из базы данных и вернув найденные записи todo.
- Реализуйте метод findById () , найдя запись todo из базы данных и вернув найденную запись todo.
- Реализуйте метод update () , выполнив следующие действия:
- Прочитайте информацию об обновленной записи todo из тела запроса.
- Проверьте информацию об обновленной записи todo.
- Обновите информацию о записи todo и верните обновленную запись todo.
- Создайте метод @ExceptionHandler, который устанавливает статус ответа 404, если запись todo не найдена ( исключение TodoNotFoundException ).
Исходный код класса TodoController выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; import java.util.List; @RestController @RequestMapping ( "/api/todo" ) final class TodoController { private final TodoService service; @Autowired TodoController(TodoService service) { this .service = service; } @RequestMapping (method = RequestMethod.POST) @ResponseStatus (HttpStatus.CREATED) TodoDTO create( @RequestBody @Valid TodoDTO todoEntry) { return service.create(todoEntry); } @RequestMapping (value = "{id}" , method = RequestMethod.DELETE) TodoDTO delete( @PathVariable ( "id" ) String id) { return service.delete(id); } @RequestMapping (method = RequestMethod.GET) List<TodoDTO> findAll() { return service.findAll(); } @RequestMapping (value = "{id}" , method = RequestMethod.GET) TodoDTO findById( @PathVariable ( "id" ) String id) { return service.findById(id); } @RequestMapping (value = "{id}" , method = RequestMethod.PUT) TodoDTO update( @RequestBody @Valid TodoDTO todoEntry) { return service.update(todoEntry); } @ExceptionHandler @ResponseStatus (HttpStatus.NOT_FOUND) public void handleTodoNotFound(TodoNotFoundException ex) { } } |
Примечание. Если проверка не удалась, наш REST API возвращает ошибки проверки в формате JSON и устанавливает статус ответа 400. Если вы хотите узнать больше об этом, прочитайте сообщение в блоге под названием: Spring from the Trenches: Добавление проверки в REST API ,
Вот и все. Теперь мы создали REST API, который обеспечивает операции CRUD для записей todo и сохраняет их в базе данных MongoDB. Давайте подведем итог тому, что мы узнали из этого поста в блоге.
Резюме
Этот пост научил нас трем вещам:
- Мы можем получить необходимые зависимости с помощью Maven, объявив только две зависимости: spring-boot-starter-web и spring-data-mongodb .
- Если нас устраивает конфигурация Spring Boot по умолчанию, мы можем настроить наше веб-приложение, используя поддержку автоматической настройки и «сбрасывая» новые файлы jar в classpath.
- Мы научились создавать простой REST API, который сохраняет информацию в базе данных MongoDB и находит информацию из нее.
Вы можете получить пример приложения к этому сообщению в блоге от Github .
Ссылка: | Создание REST API с помощью Spring Boot и MongoDB от нашего партнера по JCG Аттилы Михали Балаза в блоге Java Advent Calendar . |