Учебные материалы

Создание RESTful web-сервиса с Spring Boot Actuator

Spring Boot Actuator является подпроектом Spring Boot. Он добавляет несколько сервисов бизнес-уровня без особых усилий с вашей стороны. В этом уроке вы создадите приложение и увидите, как добавить эти сервисы.

Что вы создадите

В этом уроке освещаются шаги создания "hello world" RESTful Web Service c использованием Spring Boot Actuator. Вы создадите сервис, который принимает HTTP GET запрос:

$ curl http://localhost:9000/hello-world

А в ответ возвращает JSON:

{"id":1,"content":"Hello, World!"}

Также есть некоторые возможности для вашего приложения "из коробки" управлять сервисом в рабочем (или других) окружениях. Бизнес логика сервиса построена так же, как в Создание RESTful Web-сервиса. Вам необязательно использовать это руководство для изучения, однако это может быть интересным, чтобы впоследствии сравнить результаты.

Что вам потребуется

  • Примерно 15 минут свободного времени
  • Любимый текстовый редактор или IDE
  • JDK 7 и выше
  • Gradle 1.11+ или Maven 3.0+
  • Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.

Как проходить этот урок

Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.

Чтобы начать с нуля, перейдите в Настройка проекта.

Чтобы пропустить базовые шаги, выполните следующее:

Когда вы закончите, можете сравнить получившийся результат с образцом в gs-actuator-service/complete.

Настройка проекта

Для начала вам необходимо настроить базовый скрипт сборки. Вы можете использовать любую систему сборки, которая вам нравится для сборки проетов Spring, но в этом уроке рассмотрим код для работы с Gradle и Maven. Если вы не знакомы ни с одним из них, ознакомьтесь с соответсвующими уроками Сборка Java-проекта с использованием Gradle или Сборка Java-проекта с использованием Maven.

Создание структуры каталогов

В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру, командой mkdir -p src/main/java/hello для *nix систем:

└── src
    └── main
        └── java
            └── hello

Создание файла сборки Gradle

Ниже представлен начальный файл сборки Gradle. Файл pom.xml находится здесь. Если вы используете Spring Tool Suite (STS), то можете импортировать урок прямо из него.

Если вы посмотрите на pom.xml, вы найдете, что указана версия для maven-compiler-plugin. В общем, это не рекомендуется делать. В данном случае он предназначен для решения проблем с нашей CI системы, которая по умолчанию имеет старую(до Java 5) версию этого плагина.

build.gradle

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-release" }
        mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.8.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-actuator-service'
    version =  '0.1.0'
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "http://repo.spring.io/libs-release" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-actuator")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

Spring Boot gradle plugin предоставляет множество удобных возможностей:

  • Он собирает все jar'ы в classpath и собирает единое, исполняемое "über-jar", что делает более удобным выполнение и доставку вашего сервиса
  • Он ищет public static void main() метод, как признак исполняемого класса
  • Он предоставляет встроенное разрешение зависимостей, с определенными номерами версий для соответсвующих Spring Boot зависимостей. Вы можете переопределить на любые версии, какие захотите, но он будет по умолчанию для Boot выбранным набором версий

Запуск пустого сервиса

Ниже представлено пустое Spring MVC приложение.

src/main/java/hello/HelloWorldConfiguration.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class HelloWorldConfiguration {

    public static void main(String[] args) {
        SpringApplication.run(HelloWorldConfiguration.class, args);
    }

}

Аннотация @EnableAutoConfiguration обеспечивает загрузку по умолчанию (подобно встроенному сервлет контейнеру) в зависимости от содержания вашего classpath и других вещей. Она также ключает Spring MVC @EnableWebMvc аннотацию для активации web компонентов.

Не все компоненты определены в этом приложении, но этого достаточно для запуска и просмотра некоторых возможностей Actuator'а. Команда SpringApplication.run() знает как запустить web приложение. Все, что вам нужно, это запустить команду:

$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar

Вы ещё не писали код, так что же случится? Подождите, пока сервер запустится, перейдите в терминал и попробуйте выполнить:

$ curl localhost:8080
{"timestamp":1384788106983,"error":"Not Found","status":404,"message":""}

Итак, сервер запущен, но вы пока не определили ни одну точку выхода бизнес логики. Поэтому по умолчанию контейнер генерирует HTML ответ ошибки, вы видите общий JSON ответ из Actuator точки выхода /error. Попробуйте что-то другое, например:

$ curl localhost:8080/health
ok

Вы получили "ОК", значит все хорошо.

Более подробную информацию смотрите на Spring Boot Actuator Project.

Создание класса представления

Для начала, давайте подумаем, как будет выглядеть ваше API.

Вы хотите обрабатывать GET запросы для /hello-world, дополнительно с учетом параметра name. В ответ на этот запрос вы будете отправлять JSON, представляющий сообщение, которое выглядит примерно так:

{
    "id": 1,
    "content": "Hello, World!"
}

Поле id является уникальным идентификатором сообщения, а content - текстовым представлением.

Для моделирования представления сообщения, создайте класс представления:

src/main/java/hello/Greeting.java

package hello;

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }

}

Теперь вам нужно создать контроллер точки выхода, который будет обслуживать этот класс.

Создание контроллера ресурсов

В Spring, точки выхода REST - это просто Spring MVC контроллеры. Представленный ниже Spring MVC контроллер обрабатывает GET запрос для /hello-world и возвращает ресурс Greeting:

src/main/java/hello/HelloWorldController.java

package hello;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/hello-world")
public class HelloWorldController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping(method=RequestMethod.GET)
    public @ResponseBody Greeting sayHello(@RequestParam(value="name", required=false, defaultValue="Stranger") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }

}

Ключевое отличие между обычным(human-facing) контроллером и REST контроллером в том, как создан ответ. Вместо того, чтобы опираться на представление(такое как JSP) для отображения данных модели в HTML, REST контроллер просто возвращает данные, записанные прямо в тело ответа.

Аннотация @ResponseBody говорит Spring MVC не отображать модель в представлении, а писать возвращаемый объект в тело ответа. Делается это с помощью одного из ковертеров сообщений Spring. Т.к. Jackson 2 находится в classpath, то значит MappingJackson2HttpMessageConverter будет обрабатывать конвертацию Greeting в JSON при условии, что заголовок Accept запроса указывает, что необходимо вернуть JSON.

Откуда вы знаете, что Jackson 2 в classpath? Если запустить mvn dependency:tree или ./gradlew dependencies, то можно увидеть подробное дерево зависимостей, в котором есть Jackson 2. Вы также можете увидеть, что он идет в составе spring-boot-starter-web.

Создание выполняемым главный класс

Вы можете запустить приложение из собственного главного класса, либо мы можем сделать это напрямую из одного из конфигурационных классов. Самым простым способом является использование вспомогательного класса SpringApplication:

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

В обычном Spring MVC приложении вы должны добавлять @EnableWebMVC для включения ключевого поведения, включая конфигурацию на DispatcherServlet. Но Spring Boot подключает эту аннотацию автоматически, когда обнаруживает spring-webmvc в вашем classpath. Это позволяет собрать контроллер в последующем этапе.

Также присутствует аннотация @componentScan, которая говорит Spring сканировать пакет hello на контроллеры(вместе с другими аннотированными компонентом классами).

Сборка исполняемого JAR

Вы можете собрать единый исполняемый JAR-файл, который содержит все необходимые зависимости, классы и ресурсы. Это делает его легким в загрузке, версионировании и развертывании сервиса как приложения на протяжении всего периода разработки, на различных средах и так далее.

./gradlew build

Затем вы можете запустить JAR-файл:

java -jar build/libs/gs-actuator-service-0.1.0.jar

Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run, либо вы можете собрать приложение с mvn clean package и запустить JAR примерно так:

java -jar target/gs-actuator-service-0.1.0.jar
Процедура, описанная выше, создает исполняемый JAR. Вы также можете вместо него собрать классический WAR-файл.

Запуск сервиса

Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:

./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar
Если вы используете Maven, то можете запустить ваш сервис таким образом: mvn clean package && java -jar target/gs-actuator-service-0.1.0.jar.

Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:

./gradlew bootRun
С mvn - mvn spring-boot:run.
... service comes up ...

Протестируем:

$ curl localhost:8080/hello-world
{"id":1,"content":"Hello, Stranger!"}

Переключение на другой порт сервера

Spring Boot Actuator по умолчанию запускается на порте 8080. Добавив файл application.properties, вы можете переопределить эту настройку.

src/main/resources/application.properties

server.port: 9000
management.port: 9001
management.address: 127.0.0.1

Запустите сервер снова:

$ ./gradlew clean build && java -jar build/libs/gs-actuator-service-0.1.0.jar

... service comes up on port 9000 ...

Протестируем:

$ curl localhost:8080/hello-world
curl: (52) Empty reply from server
$ curl localhost:9000/hello-world
{"id":1,"content":"Hello, Stranger!"}

Итог

Поздравляем! Вы только что разработали простой RESTful сервис, используя Spring. Вы добавили некоторые полезные встроенные сервисы благодаря Spring Boot Actuator.

С оригинальным текстом урока вы можете ознакомиться на spring.io.

comments powered by Disqus