1. 程式人生 > >Spring Boot(一):快速開始

Spring Boot(一):快速開始

Spring Boot(一):快速開始

本系列文章旨在使用最小依賴、最簡單配置,幫助初學者快速掌握Spring Boot各元件使用,達到快速入門的目的。全部文章所使用示例程式碼均同步Github倉庫和Gitee倉庫。

1. Spring Boot是什麼?

Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。

講的通俗一點就是Spring Boot並不是一個新的框架,它只是整合和預設實現了很多框架的配置方式。

2. 好處是什麼?

最大的好處就是簡單、快捷、方便,在Spring Boot之前,我們如果要搭建一個框架需要做什麼?

  • 配置web.xml,載入Spring和Spring MVC,載入各種過濾器、攔截器
  • 在配置檔案application.xml中配置資料庫、配置快取、配置連線池等等
  • 配置日誌檔案
  • 配置各種配置檔案的讀取
  • 配置上下文、配置定時任務
  • ...
  • 各種各樣的配置
  • ...

筆者手邊正好有一個很久之前的專案,當時還是使用的Spring3.x,可以給各位看一下當時一個專案的配置檔案有多少:

而我如果需要新建一個專案,這裡面大量的配置檔案都要copy過去,並且重新除錯,非常的不方便且浪費時間,當Spring Boot橫空出世的時候,這些噩夢都結束了。

Spring Boot的優勢:

  • 為所有Spring開發者更快的入門
  • 開箱即用,提供各種預設配置來簡化專案配置
  • 內嵌式容器簡化Web專案
  • 沒有冗餘程式碼生成和XML配置的要求

3. 快速入門

目標設定:構建一個簡單的RESTful API並實現對應的單元測試

3.1 工程構建方式

Spring Boot提供兩種工程構建方式:

關於建立springcloud專案,目前有兩種比較方便的方案,核心都是一樣的,大家自行選擇自己使用方便的。

方式一:

開啟spring的官方連結:

https://start.spring.io/

在 Group 中填入自己的組織,一般填寫公司的域名的到寫,例如 com.jd 或者 com
.baidu ,這裡我直接寫 com.springboot

在 Artifact 中填寫工程的名稱,這裡我直接寫 spring-boot-quick-start 。

package 選擇 jar ,java 選擇11(目前最新的LTS版本),至此,基礎選擇已經全都選完,接下來要開始選擇我們使用的 Spring Boot 的元件了。

在 Dependencies 中找到 Spring Web ,選擇 Spring Web ,結果如下圖:

最後點選下方的綠色長條按鈕 Generate the project 進行下載,等待下載完成後,直接將壓縮包解壓匯入我們的編輯工具idea裡即可。

方式二:

基於 idea 建立,開啟 idea ,首先 file->new->project ,選中 Spring Initializr ,這時可以看到右側讓我們選擇一個初始化的服務url,預設的就是上面的官方連結,https://start.spring.io/

點選 next 下一步,填寫和上面一樣的 Group 、 Artifact 、 java 版本、 package 方式等資訊,繼續 next 下一步,選擇依賴,和前面的方法的一樣,在 Dependencies 中找到 Spring Web ,選擇 Spring Web ,點選 next ,選擇專案名稱和儲存路徑,點選 finish ,靜靜等一會,第一個專案 spring-boot-quick-start 就新鮮出爐了~~~

我一般選擇第一種方式建立 Spring Boot 專案,這種方式不依賴IDE工具。

3.2 工程結構解析

首先先看一下我們建立的工程結構,如下圖:

  • pom.xml:maven工程配置檔案,主要配置當前工程的一些基本資訊,包含我們當前使用的元件,版本等資訊。
  • src/main/java下的程式入口:Chapter1Application。
  • src/main/resources下的配置檔案:application.properties。
  • src/test/下的測試入口:Chapter1ApplicationTests。

3.3 pom.xml

這裡我們重點關注 <dependencies> 標籤,這裡寫明瞭我們引入的元件

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  • spring-boot-starter-web:Web模組
  • spring-boot-starter-test:測試模組,包括JUnit、Hamcrest、Mockito

3.4 使用 Spring MVC 實現一組對 User 物件的 RESTful API

RESTful API 設計如下:

請求型別 URL 功能
GET / 查詢使用者列表
POST / 建立User
GET /{id} 根據 url 中的 id 獲取 user 資訊
PUT /{id} 根據 id 更新使用者資訊
DELETE /{id} 根據 id 刪除使用者資訊

注意:RESTful介面在設計的時候應該遵循標準的方法以及語義,這些語義包含了安全性和冪等性等方面的考量,例如GET和HEAD請求都是安全的, 無論請求多少次,都不會改變伺服器狀態。而GET、HEAD、PUT和DELETE請求都是冪等的,無論對資源操作多少次, 結果總是一樣的,後面的請求並不會產生比第一次更多的影響。

下面列出了GET,DELETE,PUT和POST的典型用法:

GET

  • 安全且冪等
  • 獲取表示
  • 變更時獲取表示(快取)

POST

  • 不安全且不冪等
  • 使用服務端管理的(自動產生)的例項號建立資源
  • 建立子資源
  • 部分更新資源
  • 如果沒有被修改,則不過更新資源(樂觀鎖)

PUT

  • 不安全但冪等
  • 用客戶端管理的例項號建立一個資源
  • 通過替換的方式更新資源
  • 如果未被修改,則更新資源(樂觀鎖)

DELETE

  • 不安全但冪等
  • 刪除資源

使用者Model類如下:

public class UserModel {
    private Long id;
    private String name;
    private int age;
    
    // 省略 getter 和 setter
}

REST API 實現類如下:

@RestController
public class UserController {

    // 建立執行緒安全的Map,用作資料儲存
    static Map<Long, UserModel> users = new ConcurrentHashMap<>();

    /**
     * 查詢使用者列表
     * @return
     */
    @GetMapping("/")
    public List<UserModel> getUserList() {
        List<UserModel> list = new ArrayList<UserModel>(users.values());
        return list;
    }

    /**
     * 建立User
     * @param userModel
     * @return
     */
    @PostMapping("/")
    public UserModel postUser(@ModelAttribute UserModel userModel) {
        users.put(userModel.getId(), userModel);
        return users.get(userModel.getId());
    }

    /**
     * {id} 根據 url 中的 id 獲取 user 資訊
     * url中的id可通過@PathVariable繫結到函式的引數中
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public UserModel getUser(@PathVariable Long id) {
        return users.get(id);
    }

    /**
     * 根據 id 更新使用者資訊
     * @param id
     * @param userModel
     * @return
     */
    @PutMapping("/{id}")
    public UserModel putUser(@PathVariable Long id, @ModelAttribute UserModel userModel) {
        UserModel u = users.get(id);
        u.setName(userModel.getName());
        u.setAge(userModel.getAge());
        users.put(id, u);
        return users.get(userModel.getId());
    }

    /**
     * 根據 id 刪除使用者資訊
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Long id) {
        users.remove(id);
        return "success";
    }
}
  • @Controller:修飾class,用來建立處理http請求的物件
  • @RestController:Spring4之後加入的註解,原來在@Controller中返回json需要@ResponseBody來配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,預設返回json格式。

可以看一下 @RestController ,可以看到 @RestController 本身就是由 @ResponseBody@Controller 組成的,原始碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     * @since 4.0.1
     */
    @AliasFor(annotation = Controller.class)
    String value() default "";

}

單元測試類如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootQuickStartApplicationTests {

    private MockMvc mvc;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
    }

    @Test
    public void contextLoads() throws Exception {
        RequestBuilder request = null;

        // 1、get查一下user列表,應該為空
        request = MockMvcRequestBuilders.get("/")
                .contentType(MediaType.APPLICATION_JSON);
        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 2、post提交一個user
        request = MockMvcRequestBuilders.post("/")
                .param("id", "1")
                .param("name", "Spring Boot")
                .param("age", "18")
                .contentType(MediaType.APPLICATION_JSON);
        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();


        // 3、get獲取user列表,應該有剛才插入的資料
        request = MockMvcRequestBuilders.get("/")
                .contentType(MediaType.APPLICATION_JSON);
        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 4、put修改id為1的user
        request = MockMvcRequestBuilders.put("/1")
                .param("name", "Spring Boot Test")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 5、get一個id為1的user
        request = MockMvcRequestBuilders.get("/1")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 6、del刪除id為1的user
        request = MockMvcRequestBuilders.delete("/1")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 7、get查一下user列表,應該為空

        request = MockMvcRequestBuilders.get("/")
                .contentType(MediaType.APPLICATION_JSON);

        mvc.perform(request)
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

    }

}

啟動測試類,控制檯列印如下,這裡僅擷取一段內容做展示:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /
       Parameters = {id=[1], name=[Spring Boot], age=[18]}
          Headers = [Content-Type:"application/json"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.springboot.springbootquickstart.controller.UserController
           Method = public com.springboot.springbootquickstart.model.UserModel com.springboot.springbootquickstart.controller.UserController.postUser(com.springboot.springbootquickstart.model.UserModel)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json;charset=UTF-8"]
     Content type = application/json;charset=UTF-8
             Body = {"id":1,"name":"Spring Boot","age":18}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

從控制檯列印中可以完整的看到整個模擬請求的過程以及引數。

示例程式碼-Github
示例程式碼-Gitee

4. 參考

《Spring Boot(一):入門篇》
《Spring Boot構建RESTful API與單元測試