Перейти к содержанию

Введение в стартеры Spring Boot

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

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

Имеется более 30 стартеров. Рассмотрим некоторые из них в следующих разделах.

Стартер для веб-приложений

Рассмотрим разработку службы REST. Можно использовать такие библиотеки, как Spring MVC, Tomcat и Jackson – множество зависимостей для одного приложения.

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Теперь можно создать REST-контроллер. Для простоты не будем использовать базу данных и сосредоточимся на контроллере REST:

@RestController
public class GenericEntityController {
    private List<GenericEntity> entityList = new ArrayList<>();

    @RequestMapping("/entity/all")
    public List<GenericEntity> findAll() {
        return entityList;
    }

    @RequestMapping(value = "/entity", method = RequestMethod.POST)
    public GenericEntity addEntity(GenericEntity entity) {
        entityList.add(entity);
        return entity;
    }

    @RequestMapping("/entity/findby/{id}")
    public GenericEntity findById(@PathVariable Long id) {
        return entityList.stream().
                 filter(entity -> entity.getId().equals(id)).
                   findFirst().get();
    }
}

GenericEntity – это простой бин с id типа Long и значением типа String. Вот и все – при запущенном приложении можно получить доступ к http://localhost:8080/entity/all и проверить, работает ли контроллер. Мы создали приложение REST с минимальной конфигурацией.

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

Для тестирования обычно используем следующий набор библиотек: Spring Test, JUnit, Hamcrest и Mockito. Можно включить все эти библиотеки вручную, но стартер Spring Boot можно использовать для автоматического включения этих библиотек следующим образом:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Обратите внимание, что не нужно указывать номер версии артефакта. Spring Boot определит, какую версию использовать – все, что нужно указать, это версию артефакта spring-boot-starter-parent. Если позже понадобится обновить библиотеку Boot и зависимости, обновите версию Boot в одном месте, и она позаботится обо всем остальном.

Протестируем контроллер, который создали в предыдущем примере.

Проверить контроллер можно двумя способами:

  • использовать фиктивную среду (mock environment);
  • использовать встроенный контейнер сервлетов (например, Tomcat или Jetty).

В этом примере будем использовать фиктивную среду:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class SpringBootApplicationIntegrationTest {
    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;

    @Before
    public void setupMockMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void givenRequestHasBeenMade_whenMeetsAllOfGivenConditions_thenCorrect()
      throws Exception { 
        MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
        MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
        mockMvc.perform(MockMvcRequestBuilders.get("/entity/all")).
        andExpect(MockMvcResultMatchers.status().isOk()).
        andExpect(MockMvcResultMatchers.content().contentType(contentType)).
        andExpect(jsonPath("$", hasSize(4))); 
    } 
}

Приведенный выше тест вызывает конечную точку /entity/all и проверяет, что ответ JSON содержит 4 элемента. Чтобы этот тест прошел, необходимо инициализировать список в классе контроллера:

public class GenericEntityController {
    private List<GenericEntity> entityList = new ArrayList<>();

    {
        entityList.add(new GenericEntity(1l, "entity_1"));
        entityList.add(new GenericEntity(2l, "entity_2"));
        entityList.add(new GenericEntity(3l, "entity_3"));
        entityList.add(new GenericEntity(4l, "entity_4"));
    }
    //...
}

Здесь важно то, что аннотация @WebAppConfiguration и MockMVC являются частью модуля spring-test, hasSize – это сопоставитель Hamcrest, а @Before – аннотация JUnit. Все они доступны путем импорта одной этой стартовой зависимости.

Стартер Data JPA

Большинство веб-приложений имеют своего рода персистентность. Чаще всего это JPA.

Вместо того, чтобы определять все связанные зависимости вручную, воспользуемся стартером:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Обратите внимание, что из коробки имеется автоматическая поддержка как минимум следующих баз данных: H2, Derby и Hsqldb. В нашем примере будем использовать H2.

Создадим репозиторий для сущности:

public interface GenericEntityRepository extends JpaRepository<GenericEntity, Long> {}

Теперь протестируем код с помощью JUnit:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class SpringBootJPATest {
    
    @Autowired
    private GenericEntityRepository genericEntityRepository;

    @Test
    public void givenGenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() {
        GenericEntity genericEntity = 
          genericEntityRepository.save(new GenericEntity("test"));
        GenericEntity foundedEntity = 
          genericEntityRepository.findOne(genericEntity.getId());
        
        assertNotNull(foundedEntity);
        assertEquals(genericEntity.getValue(), foundedEntity.getValue());
    }
}

Мы не тратили время на указание поставщика базы данных, URL-подключения и учетных данных. Никаких дополнительных настроек не требуется, так как мы пользуемся фиксированными настройками Boot по умолчанию. Но, конечно, все эти детали можно настроить при необходимости.

Стартер для почты

Очень распространенной задачей в корпоративной разработке является отправка электронной почты, и работа напрямую с Java Mail API обычно вызывает трудности.

Стартер Spring Boot скрывает эту сложность – почтовые зависимости можно указать следующим образом:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

Теперь можно напрямую использовать JavaMailSender. Напишем несколько тестов.

Для тестирования понадобится простой SMTP-сервер. В этом примере будем использовать Wiser. Вот как можно включить его в POM:

<dependency>
    <groupId>org.subethamail</groupId>
    <artifactId>subethasmtp</artifactId>
    <version>3.1.7</version>
    <scope>test</scope>
</dependency>

Последнюю версию Wiser можно найти в центральном репозитории Maven.

Вот исходный код теста:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class SpringBootMailTest {
    @Autowired
    private JavaMailSender javaMailSender;

    private Wiser wiser;

    private String userTo = "user2@localhost";
    private String userFrom = "user1@localhost";
    private String subject = "Test subject";
    private String textMail = "Text subject mail";

    @Before
    public void setUp() throws Exception {
        final int TEST_PORT = 25;
        wiser = new Wiser(TEST_PORT);
        wiser.start();
    }

    @After
    public void tearDown() throws Exception {
        wiser.stop();
    }

    @Test
    public void givenMail_whenSendAndReceived_thenCorrect() throws Exception {
        SimpleMailMessage message = composeEmailMessage();
        javaMailSender.send(message);
        List<WiserMessage> messages = wiser.getMessages();

        assertThat(messages, hasSize(1));
        WiserMessage wiserMessage = messages.get(0);
        assertEquals(userFrom, wiserMessage.getEnvelopeSender());
        assertEquals(userTo, wiserMessage.getEnvelopeReceiver());
        assertEquals(subject, getSubject(wiserMessage));
        assertEquals(textMail, getMessage(wiserMessage));
    }

    private String getMessage(WiserMessage wiserMessage)
      throws MessagingException, IOException {
        return wiserMessage.getMimeMessage().getContent().toString().trim();
    }

    private String getSubject(WiserMessage wiserMessage) throws MessagingException {
        return wiserMessage.getMimeMessage().getSubject();
    }

    private SimpleMailMessage composeEmailMessage() {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setTo(userTo);
        mailMessage.setReplyTo(userFrom);
        mailMessage.setFrom(userFrom);
        mailMessage.setSubject(subject);
        mailMessage.setText(textMail);
        return mailMessage;
    }
}

В тесте методы @Before и @After отвечают за запуск и остановку почтового сервера.

Обратите внимание, что мы подключаем бин JavaMailSender, который был автоматически создан Spring Boot.

Как и другие значения по умолчанию в Boot, параметры электронной почты для JavaMailSender можно настроить в application.properties:

spring.mail.host=localhost
spring.mail.port=25
spring.mail.properties.mail.smtp.auth=false

Итак, мы настроили почтовый сервер на localhost:25 и не требовали аутентификации.

Заключение

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

Актуальный список стартеров можно найти здесь.

Исходный код примеров можно найти здесь.

Оригинал