1.概述
依賴管理是任何復(fù)雜項(xiàng)目的關(guān)鍵方面鹏秋。手動(dòng)完成這些操作并不理想; 你花在它上面的時(shí)間越多漩氨,你在項(xiàng)目的其他重要方面所花費(fèi)的時(shí)間就越少惦蚊。
構(gòu)建Spring Boot啟動(dòng)器是為了解決這個(gè)問題疼约。Starter POM是一組方便的依賴描述符,您可以在應(yīng)用程序中包含這些描述符怔锌。您可以獲得所需的所有Spring和相關(guān)技術(shù)的一站式服務(wù),而無需搜索示例代碼变过,并復(fù)制粘貼依賴描述符埃元。
目前有超過30個(gè)啟動(dòng)啟動(dòng)器 - 讓我們?cè)谝韵虏糠种锌吹剿鼈冎械囊恍?/p>
2.The Web Starter
首先,我們來看看開發(fā)REST服務(wù); 我們可以使用像Spring MVC媚狰,Tomcat和Jackson這樣的庫 - 對(duì)于單個(gè)應(yīng)用程序來說有很多依賴關(guān)系岛杀。
Spring Boot啟動(dòng)器可以通過添加一個(gè)依賴項(xiàng)來幫助減少手動(dòng)添加的依賴項(xiàng)的數(shù)量。因此崭孤,不是手動(dòng)指定依賴項(xiàng)类嗤,而是添加一個(gè)啟動(dòng)器,如以下示例所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
現(xiàn)在我們可以創(chuàng)建一個(gè)REST控制器辨宠。為簡(jiǎn)單起見遗锣,我們不會(huì)使用數(shù)據(jù)庫并只專注于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是一個(gè)簡(jiǎn)單的bean,包含與Long類型id屬性和String類型的value屬性嗤形。
在應(yīng)用程序運(yùn)行時(shí)精偿,您可以訪問http://localhost:8080/entity/all 并檢查控制器是否正常工作。
我們已經(jīng)創(chuàng)建了一個(gè)具有相當(dāng)小配置的REST應(yīng)用程序赋兵。
3.The Test Starter
對(duì)于測(cè)試笔咽,我們通常使用以下一組庫:Spring Test,JUnit霹期,Hamcrest和Mockito叶组。我們可以手動(dòng)包含所有這些庫,但也可以使用Spring Boot starter以下列方式自動(dòng)包含這些庫:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
請(qǐng)注意经伙,您無需指定artifact的版本號(hào)扶叉。Spring Boot將確定要使用的版本 - 您需要指定的是spring-boot-starter-parent的版本。如果以后需要升級(jí)Boot庫和依賴項(xiàng)枣氧,只需在一個(gè)地方升級(jí)Boot版本溢十,它將負(fù)責(zé)其余的工作。
讓我們實(shí)際測(cè)試我們?cè)谇耙粋€(gè)例子中創(chuàng)建的控制器达吞。
有兩種方法可以測(cè)試控制器:
- 使用模擬環(huán)境
- 使用嵌入式Servlet容器(如Tomcat或Jetty)
在這個(gè)例子中张弛,我們將使用模擬環(huán)境:
@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)));
}
}
上面的測(cè)試調(diào)用/entity/all端點(diǎn)并驗(yàn)證JSON響應(yīng)是否包含4個(gè)元素。要通過此測(cè)試酪劫,我們還必須在控制器類中初始化我們的列表:
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是一個(gè)Hamcrest匹配器,而@Before是一個(gè)JUnit注釋覆糟。這些都可以通過導(dǎo)入這一個(gè)啟動(dòng)器依賴項(xiàng)來獲得刻剥。
4. The Data JPA Starter
大多數(shù)Web應(yīng)用程序都有需要某種持久性 - 這通常是JPA。
不需要手動(dòng)定義所有相關(guān)的依賴項(xiàng) - 讓我們改為使用啟動(dòng)器:
<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>
請(qǐng)注意滩字,我們可以開箱即用自動(dòng)支持以下數(shù)據(jù)庫:H2造虏,Derby和Hsqldb。在我們的例子中麦箍,我們將使用H2漓藕。
現(xiàn)在讓我們?yōu)槲覀兊膶?shí)體創(chuàng)建存儲(chǔ)庫:
public interface GenericEntityRepository extends JpaRepository<GenericEntity, Long> {}
這是JUnit測(cè)試:
@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());
}
}
我們沒有花時(shí)間指定數(shù)據(jù)庫供應(yīng)商,URL連接和憑據(jù)挟裂。不需要額外的配置享钞,因?yàn)槲覀儚目煽康腂oot默認(rèn)值中受益; 但當(dāng)然,如有必要诀蓉,仍可配置所有這些細(xì)節(jié)栗竖。
5.The Mail Starter
企業(yè)開發(fā)中一個(gè)非常常見的任務(wù)是發(fā)送電子郵件,直接處理Java Mail API通常很困難交排。
Spring Boot啟動(dòng)程序隱藏了這種復(fù)雜性 - 可以通過以下方式指定郵件依賴項(xiàng):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
現(xiàn)在我們可以直接使用JavaMailSender划滋,所以讓我們編寫一些測(cè)試。
出于測(cè)試目的埃篓,我們需要一個(gè)簡(jiǎn)單的SMTP服務(wù)器处坪。在這個(gè)例子中,我們將使用Wiser架专。這就是我們?nèi)绾螌⑺谖覀兊腜OM中:
<dependency>
<groupId>org.subethamail</groupId>
<artifactId>subethasmtp</artifactId>
<version>3.1.7</version>
<scope>test</scope>
</dependency>
可以在Maven中央存儲(chǔ)庫中找到最新版本的Wiser 同窘。
以下是測(cè)試的源代碼:
@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;
}
}
在測(cè)試中,@Before和@After方法負(fù)責(zé)啟動(dòng)和停止郵件服務(wù)器部脚。
請(qǐng)注意想邦,我們?cè)诔绦蛑惺褂玫腏avaMailSender bean - 這個(gè)bean是由Spring Boot自動(dòng)創(chuàng)建的。
與Boot中的任何其他默認(rèn)值一樣委刘,JavaMailSender的電子郵件設(shè)置可以在application.properties中自定義:
spring.mail.host=localhost
spring.mail.port=25
spring.mail.properties.mail.smtp.auth=false
我們?cè)趌ocalhost:25上配置了郵件服務(wù)器丧没,并且不需要身份驗(yàn)證鹰椒。
6.結(jié)論
在本文中,我們概述了Starters呕童,解釋了我們?yōu)槭裁葱枰鼈兤峒剩⑻峁┝擞嘘P(guān)如何在項(xiàng)目中使用它們的示例。
讓我們回顧一下使用Spring Boot啟動(dòng)器的好處:
- 增加pom可管理性
- 生產(chǎn)環(huán)境夺饲,測(cè)試和支持的依賴配置
- 減少項(xiàng)目的總體配置時(shí)間