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的官方鏈接:
在 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 = []
從控制台打印中可以完整的看到整個模擬請求的過程以及參數。