我是小先,一個專注大數據、分佈式技術的非斜槓青年,愛Coding,愛閱讀、愛攝影,更愛生活!
源代碼倉庫:https://github.com/zhshuixian/learn-spring-boot-2
CSDN主頁:https://me.csdn.net/u010974701
上一章中主要介紹了 Spring Boot 和如何在 IDEA 中創建 Spring Boot 項目,本章將在上一章的基礎上,介紹如何運行 Spring Boot 項目,並編寫一些 RESTful API,本章主要包含如下內容:
- 運行 Spring Boot 項目
- 編寫 RESTful API 接口
- 編寫、運行單元測試
- 設置端口號和 HTTPS
- 打包成 Jar
1、運行 Spring Boot 程序
IDEA 在完成 Spring Boot 項目的依賴資源下載後,會自動配置 Spring Boot 的啟動方式。可以通過快捷鍵 "Shift + F10" ,或者直接點擊右上角的運行按鈕。如果是社區版的 Idea,可以通過直接運行 @SpringBootApplication
註解的類,這個類會在項目創建的時候自動生成。
啟動完成後,會在下方窗口出現如下提示:
Started BootApplication in 0.884 seconds (JVM running for 1.393)
打開 Postman,訪問 http://localhost:8080會出現如下提示,這不是因為程序出錯,我們還沒有編寫任何 API 接口。
1.1、Spring Boot 入口類和 @SpringBootApplication
Spring Boot 項目在新建的時候會自動生成入口類,默認名稱為 *Application,一般情況,把入口類放在 groupId + arctifactID 的 package 路徑下。入口類的 mian 方法其實就是啟動 Java 程序標準的 main 方法,SpringApplication.run(BootApplication.class, args)
表示啟動 Spring Boot 項目。
@SpringBootApplication
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
@SpringBootApplication
是 Spring Boot 的核心註解,可以寫成如下三個註解,效果是等同的:
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
@Configuration
實際上是 @Component
的再封裝,用於定義配置類,用於替換相應的 xml 配置文件,標識這個類可以用 Spring IOC 容器作為 Bean 定義的來源。被註解的類包含一個多個 @Bean
註解的方法,@Bean
註解的方法將產生一個 Bean 對象,這些方法只會調用一次,然後由 Spring 放入 IOC 容器中。
@ComponentScan
默認把當前 package 路徑作為掃描路徑,掃描並把標識了@Controller,@Service,@Repository,@Component
註解的類到 Spring 容器中。可以使用 @ComponentScan(value = "")
指定掃描路徑。
@EnableAutoConfiguration
讓 Spring Boot 根據依賴自動為當前項目進行自動配置。例如添加了 mybatis-plus-boot-starter 這個依賴,Spring Boot 會自動進行相關配置。
2、實戰 RESTful API
這一小節,將介紹實戰如何構建 RESTful API 接口,如何返回和傳入不同格式的數據。
2.1、 Hello Spring Boot
實現一個 API 接口,返回字符串 “ Hello Spring Boot”。
新建一個 HelloSpringBoot 的類,輸入如下內容,然後再次運行 Spring Boot:
@RestController
@RequestMapping("hello")
public class HelloSpringBoot {
@RequestMapping("string")
@ResponseStatus(HttpStatus.OK)
public String helloString(){
return "Hello Spring Boot";
}
}
打開 Postman,訪問 http://localhost:8080/hello/string ,可以看到返回了字符串 “ Hello Spring Boot”。
代碼解釋 這端代碼。主要使用了兩個註解和寫了一個方法返回一個字符串。
@RestController
是一個組合註解,等同於 @Controller
加 @ResponseBody
。@Controller
聲明這是一個控制器,註解的類可以接受 HTTP 請求,只使用該註解的話,視圖解析器會解析 return 返回字符對應的 HTML 、JSP 頁面等;
@ResponseBody
會直接把 return 的內容寫入 HTTP Response Body 中,視圖解析器 InternalResourceViewResolver 將不起作用。
@ResponseStatus(HttpStatus.XXX)
設置 HTTP Response 的狀態碼,默認 200。
@RequestMapping
可以註解在類上,也可以註解在方法上。用於指定請求 URL 和對應處理方法的映射。 註解在類上的 @RequestMapping("hello")
表示這個類下的所有 URL 為 /hello/ 加 註解在方法上的 RequestMapping 的值。可以通過如下方法指定 HTTP 請求方法;如果指定請求方法 method 則必須使用改方法請求,否則報 "Request method 'XXX' not supported"。
// 等同於 @GetMapping("string") 僅支持 GET 方法訪問
@RequestMapping(value = "string",method = RequestMethod.GET)
// 等同於 @PostMapping("string") 僅支持 POST 方法訪問
@RequestMapping(value = "string",method = RequestMethod.POST)
2.2 、返回 JSON 數據
上一個例子中,RESTful API 接口返回了字符串 String。對於 Spring Boot 來說,返回 JSON 數據也是相當簡單的。這一小節將實現一個返回如下 JSON 數據的接口:
{
"status": "success",
"messages": "Hello Spring Boot By JSON"
}
新建一個類 Messages:這裡使用 lombok ,記得引入 'org.projectlombok:lombok' 這個依賴、 IDEA 勾選 “Enable annotation processing ” 和安裝 lombok 插件。
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Messages {
private String status;
private String messages;
}
在 HelloSpringBoot 這個類新增方法 helloJson,然後重新運行項目:
@RequestMapping("json")
public Messages helloJson(){
return new Messages("success","Hello Spring Boot By JSON");
}
通過 Postman 訪問 http://localhost:8080/hello/json , 則返回瞭如上所示的 JSON 數據。
2.3 、傳入數據 @RequestParam
@RequestParam
是傳入 ?name0=value0&name1=value1 這中類型參數的註解,用法如下:
@RequestParam(value = "username",required = true,defaultValue = "Boot")
- value:參數名,可以不設置,默認跟方法函數的形參名相同
- required :是否必須傳入,默認 false ,設置為 true 則必須傳入這個參數,否則報錯
- defaultValue:默認值,如果設置了默認值,設置 required = true 的情況下不傳入該參數不會報錯
在 HelloSpringBoot 這個類新增方法 param ,然後重新運行項目:
@GetMapping(value = "param")
public Messages param(@RequestParam(value = "username",defaultValue = "Boot") String username){
return new Messages("success","Hello "+ username);
}
通過 Postman 使用 GET 方法訪問 http://localhost:8080/hello/param?username=xiaoxian , 改變 username 參數的值,查看不同值返回的結果。
{
"status": "success",
"messages": "Hello xiaoxian"
}
2.4 傳入數據 @PathVariable
@PathVariable
註解的作用是把 URL 路徑的變量作為參數傳入方法函數中。例如 GitHub 的個人主頁URL https://github.com/zhshuixian 就是訪問到小先的主頁。https://github.com/{username} 中的 username 就是 PathVariable。
@PathVariable(value = "username" ,name = "username",required = true)
- value:PathVariable 名稱,需要跟 RequestMapping 中括號括起來的一樣 { }
- name:PathVariable 名稱,跟 value 一樣,兩者僅需使用一個即可
- required:是否需要傳入,默認 true
在 HelloSpringBoot 這個類新增方法 pathVariable,然後重新運行項目:
@PostMapping(value = "path/{username}")
public Messages pathVariable(@PathVariable("username") String username){
return new Messages("success","Hello "+ username);
}
通過 Postman 使用 POST 方法訪問 http://localhost:8080/hello/path/username 改變 {username} 的值,查看不同值返回的結果。
{
"status": "success",
"messages": "Hello username"
}
2.5、傳入數據 @RequestBody
@RequestBody
註解的作用是接收前端傳入的 JSON 數據,假如我們需要提交如下數據:
{
"username": "user",
"password": "springboot"
}
@RequestBody(required = false)
- required : 是否必須傳入,默認為 true,設置 false 時候,如何沒有傳任何 json 數據,將不會 new 該對象,因此在使用對象的時候要注意 Null PointerException
新建一個 SysUser 的類:
@AllArgsConstructor
@Getter
@Setter
public class SysUser {
private String username;
private String password;
}
在 HelloSpringBoot 這個類新增方法 body,然後重新運行項目:
@PostMapping("body")
public Messages body(@RequestBody SysUser sysUser){
Messages messages = new Messages();
// 需要注意 Null PointerException
if(sysUser.getUsername() !=null && sysUser.getPassword() !=null &&
sysUser.getUsername().equals("user") && sysUser.getPassword().equals("springboot")){
messages.setStatus("success");
messages.setMessages("Login Success");
}else {
messages.setStatus("error");
messages.setMessages("Login Error");
}
return messages;
}
打開 Postman,使用 POST 方法訪問 http://localhost:8080/hello/body 並傳入 JSON 數據:
通過示例可以看出 @RequestBody
註解主要是接收傳入的 JSON 數據,並根據JSON 數據的 key 名稱和成員變量名稱對應,然後將 value 值通過 Setter 或者 AllArgsConstructor 構造函數賦值給對應名稱的成員變量。在 Getter 成員變量的時候一定要注意 Null PointerException。如果 @RequestBody
註解的是 String,這會 Body 的內容當做字符串賦值:
@PostMapping("text")
public String text(@RequestBody String text){
return text;
}
代碼運行效果如下:
2.6 傳入數據 Form 表單
Form 表單有application/x-www-form-urlencoded
、multipart/form-data
和 text/plain
三種方式,其中前兩種可以比較簡單的在 Spring Boot 中使用,只需把上一小節的 body 函數中的 @RequestBody
刪掉即可,同樣在 Getter 的時候要注意 Null PointerException:
@PostMapping("form")
public Messages form(SysUser sysUser){
Messages messages = new Messages();
if(sysUser.getUsername() !=null && sysUser.getPassword() !=null &&
sysUser.getUsername().equals("user") && sysUser.getPassword().equals("springboot")){
messages.setStatus("success");
messages.setMessages("Login Success");
}else {
messages.setStatus("error");
messages.setMessages("Login Error");
}
return messages;
}
重啟項目後,打開 Postman,使用 POST 方法訪問 http://localhost:8080/hello/form 並傳入 Form 表單數據,如下框起來的方式都可以使用:
對於如何上傳、下載文件,如何傳入 XML 格式的數據,這裡暫不做介紹。
3、Spring Boot 單元測試
單元測試作為開發過程中重要的一環,在於發現各小模塊內部可能存在的錯誤,提高代碼質量,這裡將使用 JUnit 5,實戰如何使用 MockMvc
和 TestRestTemplate
對剛剛構建的 RESTful API 進行單元測試。
Spring Boot 的 spring-boot-starter-test
支持 JUnit 4 和 JUnit 5 單元測試,如果你只想使用 JUnit 5 ,則應排除 JUnit 4 的依賴。
注: Spring Boot 2.2.3.RELEASE 版本新建的時候默認排除了JUnit 4 的依賴。
Gradle 排除 JUnit 4
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
Maven 排除 JUnit 4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
3.1、新建 HelloSpringBootTest 測試類
鼠標光標放在需要新建測試類 HelloSpringBoot 的中,然後按下快捷鍵 Alt + Enter ,會彈出一個選項菜單,點擊 Create Test ,IDEA 會自動在 test 目錄下的同 package 包創建 相應的測試類 HelloSpringBootTest 。
選擇 JUnit 5,勾選需要進行單元測試的函數:
打開 HelloSpringBootTest ,新增如下幾個註解:
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = BootApplication.class)
@AutoConfigureMockMvc
class HelloSpringBootTest {
// 中間部分省略
}
@ExtendWith(SpringExtension.class)
是 Spring Boot 運行 JUnit 5 所需的註解,放在測試類名之前,用來確定這個類怎麼運行的。在 Spring Boot 2.1.x 之前的版本需要此註解,之後的版本,如實戰項目用了 2.2.3 這個版本,已經在 @SpringBootTest
中了,你可以將其刪除。
@SpringBootTest
是 1.4.0 開始引入的一個測試的註解,SpringBootTest.WebEnvironment.RANDOM_PORT
表示項目運行使用隨機的端口號,classes = BootApplication.class
Spring Boot 項目的啟動類,以上兩個參數都為非必須項。
@AutoConfigureMockMvc
自動配置 MockMVC。
3.2、MockMVC 的使用
實戰如何使用 MockMVC 進行 POST、GET 請求,並對 HTTP 返回的狀態碼和 content() 中的內容進行驗證是否正確。
@Autowired private MockMvc mvc;
@Test
void testHelloString() throws Exception {
// URL 和 驗證返回內容的代碼在 mvc.perform 中
// andExpect 對返回結果進行驗證,如果不正確將視為測試案例運行失敗
this.mvc.perform(get("/hello/string")).andExpect(status().isOk())
.andExpect(content().string("Hello Spring Boot"));
}
@Test
void testHelloJson() throws Exception {
// content().json() 對返回的 JSON 數據進行驗證
this.mvc.perform(get("/hello/json")).andExpect(status().isOk())
.andExpect(content().json("{'status':'success';'messages':'Hello Spring Boot By JSON'}"));
}
@Test
void testPathVariable() throws Exception {
this.mvc.perform(post("/hello/path/xiaoxian")).andExpect(status().isOk())
.andExpect(content().json("{'status':'success';'messages':'Hello xiaoxian'}"));
}
@Test
void testForm() throws Exception {
// contentType 指定上傳數據的類型
// param(key,value) 參數的key和 value 值
this.mvc.perform(post("/hello/form")
.contentType("application/x-www-form-urlencoded")
.param("username", "user")
.param("password", "springboot"))
.andExpect(status().isOk())
.andExpect(content().json("{'status':'success';'messages':'Login Success'}"));
}
代碼解析
@Autowired
對類中的成員變量、方法和構造函數進行標註,完成自動裝配。在本代碼中,使用了此註解後,在使用 mvc
這個成員變量時,並不需要 new,交給 Spring 自動裝配。有一個 required = true/false 的參數,默認為 true。
@Test
註解是 JUnit 的註解,JUnit 5 沒有參數可以設置,其註解的 void
類型的方法將會當做測試用例,在執行測試的時候才會運行。
3.3、TestRestTemplate
實戰如何使用 TestRestTemplate進行 POST、GET 請求,上傳 JSON、String 類型的數據,並接受返回的 JSON 數據和使用 assertThat 斷言驗證數據是否正確返回。
@Test
void testParam() {
// 接收 JSON 數據
Messages messages = this.restTemplate.getForObject("/hello/param?username=xiaoxian", Messages.class);
assertThat(messages.getMessages()).isEqualTo("Hello xiaoxian");
}
@Test
void testBody() {
SysUser sysUser = new SysUser("user", "springboot");
// 上傳 JSON 數據
Messages messages = this.restTemplate.postForObject("/hello/body", sysUser, Messages.class);
assertThat(messages.getStatus()).isEqualTo("success");
assertThat(messages.getMessages()).isEqualTo("Login Success");
}
@Test
void testText() {
String string = "Hi,Spring Boot";
// 上傳 String 字符串
String result = this.restTemplate.postForObject("/hello/text", string, String.class);
assertThat(result).isEqualTo(string);
}
3.4、運行單元測試
在 IDEA 中,單元測試代碼中會自動出現綠色三角形箭頭,點擊即自動運行單元測試。點擊類名左邊的小箭頭、右鍵“Run .....”或者快捷鍵 “Ctrl + Shift + F10”,運行完成後會在下方顯示測試通過的案例和失敗的案例。
4、設置端口號和 HTTPS
Spring Boot 項目在啟動的時候默認端口號是 8080,我們可以通過修改 src/main/resources/application.properties 這個文件來自定義端口號和啟用 HTTPS。
設置端口號
在 application.properties 文件新增如下配置:
# 自定義端口號
server.port=8000
對於 HTTPS 來說,在開發項目中你可以生成自簽名證書文件,當然通過瀏覽器訪問的話會提示你此連接不安全,以及在 Postman 中你要關閉驗證 HTTPS 的功能,在部署的時候換成 CA 機構的 SSL 數字證書即可。
生成自簽名證書文件
# keytool 是 JDK 提供的自簽名證書生成工具,以下兩個命令都可以用於生成證書
# keytool -genkeypair -keystore ssl.p12 -storetype pkcs12 -validity 365
> keytool -genkey -alias worktool -keyalg RSA -keystore ssl.keystore
Enter keystore password:xiaoxian
Re-enter new password:xiaoxian
What is your first and last name?
[Unknown]: org
What is the name of your organizational unit?
[Unknown]: boot
What is the name of your organization?
[Unknown]: boot
What is the name of your City or Locality?
[Unknown]: MZ
What is the name of your State or Province?
[Unknown]: GD
What is the two-letter country code for this unit?
[Unknown]: CN
Is CN=org, OU=boot, O=boot, L=MZ, ST=GD, C=CN correct?
[no]: y
Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 90 days
for: CN=org, OU=boot, O=boot, L=MZ, ST=GD, C=CN
HTTPS 配置
# 啟用 HTTPS
server.ssl.enabled=true
server.ssl.protocol=TLS
server.ssl.key-store=classpath:ssl.keystore
# 密碼是生成證書時候的輸入的密碼
server.ssl.key-store-password=xiaoxian
重新運行項目,打開瀏覽器訪問 https://localhost:8000/hello/string。
5、打包成 Jar 文件
打開 IDEA 的 Gradle 的工具窗口,雙擊運行 bootJar:
運行完成後會在 build/lib 文件夾生成一個 Jar 文件,拷貝到部署環境,運行如下命令即可部署運行:
java -jar ./boot-0.0.1-SNAPSHOT.jar
如果覺得文章不錯,歡迎給 GitHub 倉庫打個 Stars。
歡迎關注“編程技術進階”或小先的博客,《Spring Boot 2.X 實戰》的系列文章會首投於公眾號。