大數據

Spring Boot 2.x 實戰–第一個Spring Boot程序

我是小先,一個專注大數據、分佈式技術的非斜槓青年,愛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 註解的類,這個類會在項目創建的時候自動生成。

運行 Spring Boot

啟動完成後,會在下方窗口出現如下提示:

Started BootApplication in 0.884 seconds (JVM running for 1.393)

打開 Postman,訪問 http://localhost:8080會出現如下提示,這不是因為程序出錯,我們還沒有編寫任何 API 接口。

Postman 訪問 Spring Boot

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”。

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 數據:

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;
    }

代碼運行效果如下:

Text 類型

2.6 傳入數據 Form 表單

Form 表單有application/x-www-form-urlencodedmultipart/form-datatext/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 表單數據,如下框起來的方式都可以使用:

Form表單數據

對於如何上傳、下載文件,如何傳入 XML 格式的數據,這裡暫不做介紹。

3、Spring Boot 單元測試

單元測試作為開發過程中重要的一環,在於發現各小模塊內部可能存在的錯誤,提高代碼質量,這裡將使用 JUnit 5,實戰如何使用 MockMvcTestRestTemplate 對剛剛構建的 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 。

新建 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:

bootJar

運行完成後會在 build/lib 文件夾生成一個 Jar 文件,拷貝到部署環境,運行如下命令即可部署運行:

java -jar ./boot-0.0.1-SNAPSHOT.jar

jar 文件位置

如果覺得文章不錯,歡迎給 GitHub 倉庫打個 Stars。
歡迎關注“編程技術進階”或小先的博客,《Spring Boot 2.X 實戰》的系列文章會首投於公眾號。

在這裡插入圖片描述

Leave a Reply

Your email address will not be published. Required fields are marked *