1. 概述

在复杂的项目中,依赖管理是关键环节。手动管理依赖并不理想,你在依赖管理上花费的时间越多,投入到实际开发中的时间就越少。

Spring Boot Starter 正是为了解决这个问题而设计的。Starter 提供了一组预定义的依赖项集合,可以直接包含在你的应用中。这样,你无需到处寻找和复制大量的依赖配置,就能一站式获取所有需要的 Spring 及相关技术。

我们有超过30个Boot Starter可供选择,接下来我们会分几节介绍一些。

2. Web Starter

首先,我们来看看如何开发REST服务,可以使用Spring MVC、Tomcat和Jackson等库——一个应用可能需要很多依赖。

通过Spring Boot Starter,只需添加一个依赖,就能帮助减少手动添加的依赖数量。例如,如下所示:

<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是一个简单的bean,具有类型为Longid和类型为Stringvalue

就这样——应用运行后,可以通过访问http://localhost:8080/entity/all,检查控制器是否正常工作。

我们已经创建了一个配置相对简洁的REST应用。

3. Test Starter

通常,我们会使用Spring Test、JUnit、Hamcrest和Mockito等库进行测试。手动添加这些库是可以的,但Spring Boot Starter可以自动包含它们,方式如下:

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

注意,你不需要指定依赖的具体版本。Spring Boot 会自动决定使用哪个版本——你只需要指定 spring-boot-starter-parent 的版本。如果后期需要升级 Boot 库和依赖,只需在一个地方更新 Boot 版本,其余的它会自动处理。

让我们实际测试上一节创建的控制器。

测试控制器有两种方法:

  • 使用模拟环境
  • 使用嵌入式Servlet容器(如Tomcat或Jetty)

在这个例子中,我们将使用模拟环境:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class SpringBootApplicationIntegrationTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

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

    @Test
    public void givenRequestHasBeenMade_whenMeetsAllOfGivenConditions_thenCorrect()
      throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/entity/all"))
          .andExpect(MockMvcResultMatchers.status().isOk())
          .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
          .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 匹配器,@BeforeEach 是 JUnit 注解。这些全都在导入一个 Starter 依赖时可用。

4. Data JPA Starter

大多数 Web 应用都需要某种持久化,而 JPA 通常是首选。

不必手动定义所有相关的依赖,而是使用 Starter:

<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和Hsqlldb等数据库的自动支持。在这个例子中,我们将使用H2。

现在,让我们为实体创建仓库:

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

现在是时候测试代码了。这是JUnit测试:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
public class SpringBootJPAIntegrationTest {
    @Autowired
    private GenericEntityRepository genericEntityRepository;

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

我们没有花费时间指定数据库供应商、URL 连接和凭据。得益于 Spring Boot 的默认配置,我们无需额外设置;当然,如果需要,这些细节仍然可以自定义。

5. Mail Starter

在企业开发中,发送电子邮件是一项非常常见的任务,直接处理 Java Mail API 通常会很复杂。

Spring Boot Starter 隐藏了这种复杂性,邮件依赖可以通过以下方式指定:

<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中央仓库找到。

这里是测试的源代码:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
public class SpringBootMailIntegrationTest {
    @Autowired
    private JavaMailSender javaMailSender;

    private Wiser wiser;

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

    @BeforeEach
    public void setUp() {
        final int TEST_PORT = 8025;
        wiser = Wiser.port(TEST_PORT);
        wiser.start();
    }

    @AfterEach
    public void tearDown() {
        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;
    }
}

在测试中,@BeforeEach@AfterEach方法负责启动和停止邮件服务器。

请注意,我们正在将 JavaMailSender bean 进行绑定——这个 bean 是由 Spring Boot 自动创建的

就像 Boot 的其他默认设置一样,JavaMailSender 的邮件设置可以在 application.properties 中进行自定义:

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

因此,我们在 localhost:25 上配置了邮件服务器,并且不需要身份验证。

6. 总结

在这篇文章中,我们概述了 Starter 的功能,解释了为什么需要它们,并提供了如何在项目中使用它们的例子。

让我们总结一下使用 Spring Boot Starter 的好处:

  • 提高 pom.xml 的管理性
  • 生产就绪的、经过测试和支持的依赖配置
  • 减少项目的整体配置时间

完整的 Starter 列表可以在这里找到。示例代码可以从这里获取。


原始标题:Intro to Spring Boot Starters | Baeldung