1. Обзор
Groovy имеет ряд возможностей, которые мы могли бы захотеть использовать в наших веб-приложениях Spring.
Итак, в этом руководстве мы создадим простое приложение todo с помощью Spring Boot и Groovy. Кроме того, мы изучим их точки интеграции.
2. Приложение Todo
Наше приложение будет иметь следующие функции:
- Создать задачу
- Изменить задачу
- Удалить задачу
- Посмотреть конкретную задачу
- Посмотреть все задачи
Это будет приложение на основе REST, и мы будем использовать Maven в качестве инструмента сборки .
2.1. Зависимости Maven
Давайте включим все необходимые зависимости в наш файл pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<scope>runtime</scope>
</dependency>
Здесь мы включаем spring -boot-starter-web
для создания конечных точек REST и импортируем зависимость groovy
, чтобы обеспечить поддержку Groovy для нашего проекта .
Для уровня сохраняемости мы используем spring-boot-starter-data-jpa
, а h2
— это встроенная база данных .
Кроме того, мы должны включить gmavenplus-plugin
со всеми целями в pom.xml:
<build>
<plugins>
//...
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.9.0</version>
<executions>
<execution>
<goals>
<goal>addSources</goal>
<goal>addTestSources</goal>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>compileTests</goal>
<goal>removeStubs</goal>
<goal>removeTestStubs</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2.2. Класс сущности JPA
Давайте напишем простой класс Todo
Groovy с тремя полями — id
, task
и isCompleted
:
@Entity
@Table(name = 'todo')
class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id
@Column
String task
@Column
Boolean isCompleted
}
Здесь поле id
— это уникальный идентификатор задачи. task
содержит сведения о задаче, а isCompleted
показывает, завершена задача или нет.
Обратите внимание, что если мы не предоставляем модификаторы доступа к полю, компилятор Groovy сделает это поле закрытым, а также сгенерирует для него методы получения и установки .
2.3. Уровень сохранения
Давайте создадим интерфейс Groovy — TodoRepository
, который реализует JpaRepository
. Он позаботится обо всех операциях CRUD в нашем приложении:
@Repository
interface TodoRepository extends JpaRepository<Todo, Integer> {}
2.4. Сервисный уровень
Интерфейс TodoService
содержит все абстрактные методы, необходимые для нашей операции CRUD :
interface TodoService {
List<Todo> findAll()
Todo findById(Integer todoId)
Todo saveTodo(Todo todo)
Todo updateTodo(Todo todo)
Todo deleteTodo(Integer todoId)
}
TodoServiceImpl — это класс реализации, который
реализует все методы TodoService:
@Service
class TodoServiceImpl implements TodoService {
//...
@Override
List<Todo> findAll() {
todoRepository.findAll()
}
@Override
Todo findById(Integer todoId) {
todoRepository.findById todoId get()
}
@Override
Todo saveTodo(Todo todo){
todoRepository.save todo
}
@Override
Todo updateTodo(Todo todo){
todoRepository.save todo
}
@Override
Todo deleteTodo(Integer todoId){
todoRepository.deleteById todoId
}
}
2.5. Уровень контроллера
Теперь давайте определим все REST API в TodoController
, который является нашим @RestController
:
@RestController
@RequestMapping('todo')
public class TodoController {
@Autowired
TodoService todoService
@GetMapping
List<Todo> getAllTodoList(){
todoService.findAll()
}
@PostMapping
Todo saveTodo(@RequestBody Todo todo){
todoService.saveTodo todo
}
@PutMapping
Todo updateTodo(@RequestBody Todo todo){
todoService.updateTodo todo
}
@DeleteMapping('/{todoId}')
deleteTodo(@PathVariable Integer todoId){
todoService.deleteTodo todoId
}
@GetMapping('/{todoId}')
Todo getTodoById(@PathVariable Integer todoId){
todoService.findById todoId
}
}
Здесь мы определили пять конечных точек, которые пользователь может вызывать для выполнения операций CRUD.
2.6. Начальная загрузка приложения Spring Boot
Теперь давайте напишем класс с основным методом, который будет использоваться для запуска нашего приложения:
@SpringBootApplication
class SpringBootGroovyApplication {
static void main(String[] args) {
SpringApplication.run SpringBootGroovyApplication, args
}
}
Обратите внимание, что в Groovy использование круглых скобок необязательно при вызове метода путем передачи аргументов — и это то, что мы делаем в приведенном выше примере.
Кроме того, суффикс .class не
нужен ни для одного класса в Groovy , поэтому мы используем SpringBootGroovyApplication
напрямую.
Теперь давайте определим этот класс в pom.xml
как начальный класс
:
<properties>
<start-class>com.foreach.app.SpringBootGroovyApplication</start-class>
</properties>
3. Запуск приложения
Наконец, наше приложение готово к запуску. Мы должны просто запустить класс SpringBootGroovyApplication
как приложение Java или запустить сборку Maven:
spring-boot:run
Это должно запустить приложение на http://localhost:8080
, и мы сможем получить доступ к его конечным точкам.
4. Тестирование приложения
Наше приложение готово к тестированию. Давайте создадим класс Groovy — TodoAppTest
для тестирования нашего приложения.
4.1. Начальная настройка
Давайте определим три статические переменные — API_ROOT
, readTodoId
и writeTodoId
в нашем классе:
static API_ROOT = "http://localhost:8080/todo"
static readingTodoId
static writingTodoId
Здесь API_ROOT
содержит корневой URL нашего приложения. « readingTodoId
» и « writingTodoId»
— это первичные ключи наших тестовых данных, которые мы будем использовать позже для выполнения тестирования.
Теперь давайте создадим еще один метод — populateDummyData ()
, используя аннотацию @BeforeClass
для заполнения тестовых данных:
@BeforeClass
static void populateDummyData() {
Todo readingTodo = new Todo(task: 'Reading', isCompleted: false)
Todo writingTodo = new Todo(task: 'Writing', isCompleted: false)
final Response readingResponse =
RestAssured.given()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(readingTodo).post(API_ROOT)
Todo cookingTodoResponse = readingResponse.as Todo.class
readingTodoId = cookingTodoResponse.getId()
final Response writingResponse =
RestAssured.given()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(writingTodo).post(API_ROOT)
Todo writingTodoResponse = writingResponse.as Todo.class
writingTodoId = writingTodoResponse.getId()
}
Мы также заполним переменные — readTodoId
и writeTodoId
в одном и том же методе, чтобы сохранить первичный ключ записей, которые мы сохраняем.
Обратите внимание, что в Groovy мы также можем инициализировать bean-компоненты, используя именованные параметры и конструктор по умолчанию , как мы делаем для bean-компонентов, таких как readTodo
и writeTodo
в приведенном выше фрагменте.
4.2. Тестирование операций CRUD
Далее найдем все задачи из todo list:
@Test
void whenGetAllTodoList_thenOk(){
final Response response = RestAssured.get(API_ROOT)
assertEquals HttpStatus.OK.value(),response.getStatusCode()
assertTrue response.as(List.class).size() > 0
}
Затем давайте найдем конкретную задачу, передав readTodoId
, который мы заполнили ранее:
@Test
void whenGetTodoById_thenOk(){
final Response response =
RestAssured.get("$API_ROOT/$readingTodoId")
assertEquals HttpStatus.OK.value(),response.getStatusCode()
Todo todoResponse = response.as Todo.class
assertEquals readingTodoId,todoResponse.getId()
}
Здесь мы использовали интерполяцию для объединения строки URL.
Кроме того, давайте попробуем обновить задачу в списке задач с помощью readTodoId
:
@Test
void whenUpdateTodoById_thenOk(){
Todo todo = new Todo(id:readingTodoId, isCompleted: true)
final Response response =
RestAssured.given()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(todo).put(API_ROOT)
assertEquals HttpStatus.OK.value(),response.getStatusCode()
Todo todoResponse = response.as Todo.class
assertTrue todoResponse.getIsCompleted()
}
А затем удалите задачу из списка задач с помощью writeTodoId
:
@Test
void whenDeleteTodoById_thenOk(){
final Response response =
RestAssured.given()
.delete("$API_ROOT/$writingTodoId")
assertEquals HttpStatus.OK.value(),response.getStatusCode()
}
Наконец, мы можем сохранить новую задачу:
@Test
void whenSaveTodo_thenOk(){
Todo todo = new Todo(task: 'Blogging', isCompleted: false)
final Response response =
RestAssured.given()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(todo).post(API_ROOT)
assertEquals HttpStatus.OK.value(),response.getStatusCode()
}
5. Вывод
В этой статье мы использовали Groovy и Spring Boot для создания простого приложения. Мы также увидели, как их можно интегрировать вместе, и продемонстрировали некоторые интересные функции Groovy на примерах.
Как всегда, полный исходный код примера доступен на GitHub .