Статьи

Экстремальная лень: разработка сервисов JAX-RS с Spring Boot

Я думаю, было бы справедливо заявить, что мы, как разработчики программного обеспечения, всегда ищем способы написать меньше кода, который делает больше вещей, автоматически или нет. В связи с этим проект Spring Boot , являющийся гордым участником портфолио Spring , нарушил традиционные подходы, резко ускорив и упростив разработку приложений на основе Spring .

О Spring Boot можно многое сказать, о внутренних деталях его работы и его плавной интеграции с большинством, если не со всеми проектами Spring . Но его возможности выходят далеко за рамки этого, поддерживая первоклассную интеграцию с популярными средами Java.

В этой статье мы рассмотрим, как мы можем использовать Spring Boot в сочетании с проектом Apache CXF для быстрой разработки REST (ful) веб-сервисов . Как мы увидим очень скоро, Spring Boot позаботится о большом количестве шаблонов, что позволит нам сосредоточиться на тех частях приложения, которые действительно имеют ценность. Надеемся, что в конце этого поста преимущества использования Spring Boot для ваших проектов станут очевидными.

После этого давайте начнем с разработки простого веб-сервиса REST (ful) для управления персоналом, включенного в привычный ресурс PeopleRestService JAX-RS :

1
2
3
4
5
6
7
8
9
@Path("/people")
@Component
public class PeopleRestService {
    @GET
    @Produces({MediaType.APPLICATION_JSON})
    public Collection<Person> getPeople() {
        return Collections.singletonList(new Person("[email protected]", "John", "Smith"));
    }
}

Добавлять особо нечего, довольно простая реализация, которая возвращает жестко запрограммированную коллекцию людей. Мы можем упаковать и развернуть этот сервис JAX-RS несколькими способами, но, пожалуй, самый простой из них — разместить его во встроенном контейнере сервлетов, таком как Tomcat , Jetty или Undertow . С этим приходит рутина: инициализация контейнера, настройка местоположений контекста Spring , регистрация слушателей,… Давайте посмотрим, как Spring Boot может помочь здесь, рассмотрев конфигурацию контекста Spring ниже.

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
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackageClasses = PeopleRestService.class)
public class AppConfig {
    @Autowired private PeopleRestService peopleRestService;
  
    @Bean(destroyMethod = "shutdown")
    public SpringBus cxf() {
        return new SpringBus();
    }
 
    @Bean(destroyMethod = "destroy") @DependsOn("cxf")
    public Server jaxRsServer() {
        final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
 
        factory.setServiceBean(peopleRestService);
        factory.setProvider(new JacksonJsonProvider());
        factory.setBus(cxf());
        factory.setAddress("/");
 
        return factory.create();
    }
 
    @Bean
    public ServletRegistrationBean cxfServlet() {
        final ServletRegistrationBean servletRegistrationBean =
            new ServletRegistrationBean(new CXFServlet(), "/api/*");
        servletRegistrationBean.setLoadOnStartup(1);
        return servletRegistrationBean;
    }
}

Класс AppConfig выглядит как типичная конфигурация на основе Spring Java, за исключением этой необычной аннотации @EnableAutoConfiguration , которая без удивления приходит из модуля Spring Boot . Под капотом эта аннотация обеспечивает сложный и интеллектуальный процесс предположения, среди прочего, о том, какое приложение мы будем запускать и какие компоненты Spring нам могут понадобиться для нашего приложения. С этой конфигурацией нам просто нужен бегун для нашего приложения, также с небольшим количеством Spring Boot :

1
2
3
4
5
6
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(AppConfig.class, args);
    }
}

Имея мета-аннотацию @SpringBootApplication и используя SpringApplication для инициализации нашего контекста Spring , у нас есть полноценное работающее Java-приложение, которое можно запустить из Apache Maven с помощью плагина Spring Boot :

1
mvn spring-boot:run

Или упакован как один работающий Uber- JAR и вызывается из командной строки:

1
2
mvn package
java -jar target/jax-rs-2.0-cxf-spring-boot-0.0.1-SNAPSHOT.jar

И это все, просто пара аннотаций вместе с одной строкой кода ( основной метод). Запустив приложение, мы сможем убедиться, что наш веб-сервис REST (ful) для управления персоналом правильно развернут и полностью функционирует:

1
2
3
4
5
6
7
8
$ curl -i http://localhost:8080/api/people
 
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked
Server: Jetty(9.3.8.v20160314)
 
[{"email":"[email protected]","firstName":"John","lastName":"Smith"}]

На данный момент вы можете спросить, как это работает? Мы нигде не имели дело с контейнером сервлетов, так почему же Jetty обслуживает наши запросы? По правде говоря, нам нужно только включить наш контейнер выбора в качестве зависимости, например, используя файл Apache Maven pom.xml :

1
2
3
4
5
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.3.8.v20160314</version>
</dependency>

Spring Boot вместе с @ EnableAutoConfiguration / @ SpringBootApplication делает все остальное: он обнаруживает присутствие Jetty в пути к классам, приходит к обоснованному выводу, что мы намерены запустить веб-приложение и дополнить контекст Spring необходимыми элементами. Разве это не просто великолепно?

Было бы несправедливо заканчивать, не освещая еще одну важную особенность проекта Spring Boot : поддержку интеграционного тестирования. В этом отношении Spring Boot использует тот же подход и предоставляет несколько аннотаций, чтобы снять все строительные леса, которые мы должны были бы написать сами, в противном случае. Например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = AppConfig.class)
@WebIntegrationTest(randomPort = true)
public class PeopleRestServiceIntegrationTest {
    @Value("${local.server.port}") private int port;
  
    @Before
    public void setUp() {
        RestAssured.port = port;
    }
  
    @Test
    public void testListOfPersonsIsBeingReturnedSuccessfuly() {
        given()
            .when()
            .contentType(ContentType.JSON)
            .get("/api/people")
            .then()
            .statusCode(200)
            .log()
            .ifValidationFails();
    }
}

Всего две аннотации: @SpringApplicationConfiguration (обратите внимание, что мы используем ту же конфигурацию в тесте, что и для основного приложения) и @WebIntegrationTest (которая учитывает специфику тестирования веб-приложения и запускает встроенный контейнер сервлета на случайном порте), и у нас есть полноценный интеграционный тест с нашей службой управления JAX-RS . Порт, на котором работает контейнер сервлета, доступен через свойство среды local.server.port, поэтому мы можем настроить REST-гарантированный в фоновом режиме тестирования. Легко и просто.

В этой статье мы только что рассмотрели один конкретный случай использования Spring Boot для увеличения скорости разработки ваших проектов JAX-RS . С Spring Boot многие вещи становятся очень тривиальными, с каждым разом добавляется все больше и больше интеллекта, не говоря уже об отличной интеграции с выбранной вами IDE. Я надеюсь, что вы действительно взволнованы Spring Boot и хотите узнать больше об этом. Это стоит времени и усилий.

Полный проект доступен на Github .