baeldung 2016-07-14
1. 概述
依赖管理是任何复杂项目中的关键环节。手动进行依赖管理并不是理想的做法——你花在依赖管理上的时间越多,用于项目其他重要方面的时间就越少。
Spring Boot Starter 正是为了应对这一问题而设计的。Starter POM 是一组便捷的依赖描述符,你可以直接将其包含到你的应用程序中。通过使用 Starter,你无需四处查找示例代码、复制粘贴大量依赖项,就能一站式获取所需的所有 Spring 及相关技术依赖。
目前我们有超过 30 个 Spring Boot Starter 可供使用,接下来我们将逐一介绍其中一些常用 Starter。
2. Web Starter
首先,让我们看看如何开发一个 REST 服务。通常我们会用到 Spring MVC、Tomcat 和 Jackson 等库——对于一个单一应用来说,这涉及大量的依赖项。
Spring Boot Starter 可以帮助我们大幅减少手动添加的依赖数量,只需引入一个 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 是一个简单的 Java Bean,包含一个 Long 类型的 id 和一个 String 类型的 value。
就这样!启动应用后,访问 http://localhost:8080/entity/all 即可验证控制器是否正常工作。
我们仅用极少的配置就构建了一个完整的 REST 应用。
3. Test Starter
在测试时,我们通常会用到以下库:Spring Test、JUnit、Hamcrest 和 Mockito。虽然可以手动逐个引入这些依赖,但更高效的方式是使用 Spring Boot Test Starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
注意:你无需指定每个依赖的版本号。Spring Boot 会自动推断应使用的版本——你只需在父 POM 中指定 spring-boot-starter-parent 的版本即可。日后如需升级 Spring Boot 及其依赖,只需更新一处版本号,其余依赖将自动同步。
接下来,我们对上一节创建的控制器进行测试。
测试控制器有两种方式:
- 使用模拟环境(Mock Environment)
- 使用内嵌的 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>
开箱即用,Spring Boot 自动支持 H2、Derby 和 Hsqldb 等内存数据库。本例中我们使用 H2。
接下来创建实体的 Repository:
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 Central 仓库中找到。
测试代码如下:
@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 自动创建。
与 Spring Boot 的其他默认配置一样,邮件设置也可在 application.properties 中自定义:
spring.mail.host=localhost
spring.mail.port=25
spring.mail.properties.mail.smtp.auth=false
这里我们将邮件服务器配置为 localhost:25,且无需身份验证。
6. 结论
本文介绍了 Spring Boot Starter 的基本概念,解释了为何需要它们,并通过多个示例展示了如何在项目中使用。
使用 Spring Boot Starter 的主要优势包括:
- 提升 POM 文件的可维护性
- 提供生产就绪、经过测试且受支持的依赖配置
- 显著减少项目整体配置时间
通过合理使用 Starter,开发者可以更专注于业务逻辑,而非繁琐的依赖和配置管理。