spring boot基礎學習教程

Spring boot

標籤(空格分隔): springbootcss


HelloWorld

什麼是spring boot

Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員再也不須要定義樣板化的配置。用個人話來理解,就是spring boot其實不是什麼新的框架,它默認配置了不少框架的使用方式,就像maven整合了全部的jar包,spring boot整合了全部的框架(不知道這樣比喻是否合適)。html

使用spring boot有什麼好處

其實就是簡單、快速、方便!平時若是咱們須要搭建一個spring web項目的時候須要怎麼作呢?前端

  1. 配置web.xml,加載spring和spring mvc
  1. 配置數據庫鏈接、配置spring事務
  2. 配置加載配置文件的讀取,開啓註解
  3. 配置日誌文件
  4. 配置完成以後部署tomcat 調試

如今很是流行微服務,若是我這個項目僅僅只是須要發送一個郵件,若是個人項目僅僅是生產一個積分;我都須要這樣折騰一遍!vue

可是若是使用spring boot呢?java

很簡單,我僅僅只須要很是少的幾個配置就能夠迅速方便的搭建起來一套web項目或者是構建一個微服務!mysql

因此spring boot的優勢爲:react

  1. 爲全部Spring開發者更快的入門
  2. 開箱即用,提供各類默認配置來簡化項目配置
  3. 內嵌式容器簡化Web項目
  4. 沒有冗餘代碼生成和XML配置的要求

快速入門

本節主要目標完成Spring Boot基礎項目的構建,而且實現一個簡單的Http請求處理,經過這個例子對Spring Boot有一個初步的瞭解,並體驗其結構簡單、開發快速的特性。web

系統要求:

  • Java 7 及以上
  • Spring Framework 4.1.5及以上

本教材採用Java 1.8.0_131Spring Boot 1.5.10實現。spring

雖然JDK目前已經發布1.9版本,可是目前在互聯網公司中,使用JDK1.8版本的更多,而傳統軟件公司中不少還停留在JDK1.6,JDK1.7,甚至還有JDK1.5的。
spring boot也在2018年3月發佈了2.0的正式版本,可是對如今來講仍是太新。sql

使用Maven構建項目

經過SPRING INITIALIZR工具產生基礎項目

訪問:http://start.spring.io/

選擇構建工具Maven ProjectSpring Boot版本1.5.10以及一些工程基本信息,可參考下圖所示:

image_1c8k6uradr0b1o941ucu1ui11m879.png-96.8kB

點擊Generate Project下載項目壓縮包,解壓項目包,並用IDE以Maven項目導入,以IntelliJ IDEA 爲例:

菜單中選擇File–>New–>Project from Existing Sources...

選擇解壓後的項目文件夾,點擊OK

點擊Import project from external model並選擇Maven,點擊Next到底爲止。

若你的環境有多個版本的JDK,注意到選擇Java SDK的時候請選擇Java 7`以上的版本

項目結構解析

前面構建的spring boot項目的目錄結構以下:

image_1c8k7cbgin3upiq1tkbl2fnhk23.png-31.1kB

經過上面步驟完成了基礎項目的建立,如上圖所示,Spring Boot的基礎結構共三個文件(具體路徑根據用戶生成項目時填寫的Group全部差別):

src/main/java下的程序入口:SbDemoApplication.java
src/main/resources下的配置文件:application.properties
src/test/下的測試入口:SbDemoApplicationTests.java

生成的SbDemoApplication.javaSbDemoApplicationTests.java類均可以直接運行來啓動當前建立的項目,因爲目前該項目未配合任何數據訪問或Web模塊,程序會在加載完Spring以後結束運行。

引入Web模塊

當前的pom.xml內容以下,僅引入了兩個模塊:

spring-boot-starter:核心模塊,包括自動配置支持、日誌和YAML
spring-boot-starter-test:測試模塊,包括JUnit、Hamcrest、Mockito

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

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

引入Web模塊,需添加spring-boot-starter-web模塊:

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

編寫HelloWorld服務

建立package命名爲com.dengcl.sb_demo.web(根據實際狀況修改)
建立HelloController類,內容以下

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String sayHello() {
        return "Hello World";
    }

}

啓動主程序,執行src/main/java下的程序入口:SbDemoApplication.java,在控制檯出現以下圖內容:
image_1c8k80iug1h7o1cjt5f6vffv5n2g.png-210.6kB

打開瀏覽器訪問http://localhost:8080/hello,能夠看到頁面輸出Hello World

編寫單元測試用例·

打開的src/test/下的測試入口SbDemoApplicationTests.java類。下面編寫一個簡單的單元測試來模擬http請求,具體以下:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SbDemoApplicationTests {

    @Autowired
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
        mvc.perform(get("/hello").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Hello World!")));
    }

}

注意引入下面內容,讓statuscontentequalTo函數可用

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

至此已完成目標,經過Maven構建了一個空白Spring Boot項目,再經過引入web模塊實現了一個簡單的請求處理。

使用IDEA中的Spring Initializr來快速構建Spring Boot/Cloud工程

前面提到使用SPRING INITIALIZR頁面工具來建立spring boot項目,接下來將介紹嵌入的IDEA中的Spring Initializr工具,它同Web提供的建立功能同樣,能夠幫助咱們快速的構建出一個基礎的Spring Boot工程

菜單欄中選擇File=>New=>Project..,咱們能夠看到以下圖所示的建立功能窗口。其中Initial Service Url指向的地址就是Spring官方提供的Spring Initializr工具地址,因此這裏建立的工程實際上也是基於它的Web工具來實現的。

image_1c8ka1qrocs0po943n1ebqmb03n.png-61.3kB

點擊Next,等待片刻後,咱們能夠看到以下圖所示的工程信息窗口,在這裏咱們能夠編輯咱們想要建立的工程信息。其中,Type能夠改變咱們要構建的工程類型,好比:MavenGradle;Language能夠選擇:JavaGroovyKotlin

image_1c8ka2a2i1fgqd301tcd7nu25i44.png-40kB

點擊Next,進入選擇Spring Boot版本和依賴管理的窗口。在這裏值的咱們關注的是,它不只包含了Spring Boot Starter POMs中的各個依賴,還包含了Spring Cloud的各類依賴。

image_1c8ka3piv1vqu1j9n1h2h1ea38mb4h.png-69.8kB

點擊Next,進入最後關於工程物理存儲的一些細節。最後,點擊Finish就能完成工程的構建了。

image_1c8ka4q9118tn1qbkuqf74q120p4u.png-34.4kB

IDEA中的Spring Initializr雖然仍是基於官方Web實現,可是經過工具來進行調用並直接將結果構建到咱們的本地文件系統中,讓整個構建流程變得更加順暢,尚未體驗過此功能的Spring Boot/Cloud愛好者們不妨能夠嘗試一下這種不一樣的構建方式。

Spring Boot構建RESTful API與單元測試

首先,回顧並詳細說明一下在前面使用的@Controller@RestController@RequestMapping註解。能夠發現這些註解都和前面學習的Spring MVC中一致,其實spring boot的web實現就是經過spring mvc來實現的。

@Controller:修飾class,用來建立處理http請求的對象
@RestController:Spring4以後加入的註解,原來在@Controller中返回json須要@ResponseBody來配合,若是直接用@RestController替代@Controller就不須要再配置@ResponseBody,默認返回json格式。
@RequestMapping:配置url映射

下面咱們嘗試使用Spring MVC來實現一組對User對象操做的RESTful API,配合註釋詳細說明在Spring MVC中如何映射HTTP請求、如何傳參、如何編寫單元測試。

RESTful API具體設計以下:

image_1c8kb4b8p145gm8f11i0hvv57f9.png-82.8kB

User實體定義:

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

實現對User對象的操做接口UserController

@RestController
@RequestMapping(value="/users")     // 經過這裏配置使下面的映射都在/users下
public class UserController { 
 
    // 建立線程安全的Map 
    static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());
 
    @RequestMapping(value="/", method= RequestMethod.GET) 
    public List<User> getUserList() { 
        // 處理"/users/"的GET請求,用來獲取用戶列表 
        // 還能夠經過@RequestParam從頁面中傳遞參數來進行查詢條件或者翻頁信息的傳遞 
        List<User> r = new ArrayList<User>(users.values()); 
        return r; 
    } 
 
    @RequestMapping(value="/", method=RequestMethod.POST) 
    public String postUser(@ModelAttribute User user) { 
        // 處理"/users/"的POST請求,用來建立User 
        // 除了@ModelAttribute綁定參數以外,還能夠經過@RequestParam從頁面中傳遞參數 
        users.put(user.getId(), user); 
        return "success"; 
    } 
 
    @RequestMapping(value="/{id}", method=RequestMethod.GET) 
    public User getUser(@PathVariable Long id) { 
        // 處理"/users/{id}"的GET請求,用來獲取url中id值的User信息 
        // url中的id可經過@PathVariable綁定到函數的參數中 
        return users.get(id); 
    } 
 
    @RequestMapping(value="/{id}", method=RequestMethod.PUT) 
    public String putUser(@PathVariable Long id, @ModelAttribute User user) { 
        // 處理"/users/{id}"的PUT請求,用來更新User信息 
        User u = users.get(id); 
        u.setName(user.getName()); 
        u.setAge(user.getAge()); 
        users.put(id, u); 
        return "success"; 
    } 
 
    @RequestMapping(value="/{id}", method=RequestMethod.DELETE) 
    public String deleteUser(@PathVariable Long id) { 
        // 處理"/users/{id}"的DELETE請求,用來刪除User 
        users.remove(id); 
        return "success"; 
    } 
 
}

針對該Controller編寫測試用例驗證正確性,具體以下。固然也能夠經過瀏覽器插件等進行請求提交驗證。

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.equalTo;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
    @Autowired
    private MockMvc mvc;
    @Test
    public void testUserController() throws Exception {
        // 測試UserController
        RequestBuilder request = null;
        // 一、get查一下user列表,應該爲空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));
        // 二、post提交一個user
        request = post("/users/")
                .param("id", "1")
                .param("name", "測試大師")
                .param("age", "20");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));
        // 三、get獲取user列表,應該有剛纔插入的數據
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"測試大師\",\"age\":20}]")));
        // 四、put修改id爲1的user
        request = put("/users/1")
                .param("name", "測試終極大師")
                .param("age", "30");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));
        // 五、get一個id爲1的user
        request = get("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("{\"id\":1,\"name\":\"測試終極大師\",\"age\":30}")));
        // 六、del刪除id爲1的user
        request = delete("/users/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));
        // 七、get查一下user列表,應該爲空
        request = get("/users/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[]")));
    }

至此,咱們經過引入web模塊(沒有作其餘的任何配置),就能夠輕鬆利用Spring MVC的功能,以很是簡潔的代碼完成了對User對象的RESTful API的建立以及單元測試的編寫。其中同時介紹了Spring MVC中最爲經常使用的幾個核心註解:@Controller,@RestController,RequestMapping以及一些參數綁定的註解:@PathVariable,@ModelAttribute,@RequestParam等。


使用Thymeleaf模板引擎渲染web視圖

在前面章節中咱們完成了一個簡單的RESTful Service,體驗了快速開發的特性。可是如何把處理結果渲染到頁面上呢?那麼本篇就在上篇基礎上介紹一下如何進行Web應用的開發。

靜態資源訪問

在咱們開發Web應用的時候,須要引用大量的js、css、圖片等靜態資源。

默認配置

Spring Boot默認提供靜態資源目錄位置需置於classpath下,目錄名需符合以下規則:

  • /static
  • /public
  • /resources
  • /META-INF/resources

舉例:咱們能夠在src/main/resources/目錄下建立static,在該位置放置一個圖片文件。啓動程序後,嘗試訪問http://localhost:8080/D.jpg。如能顯示圖片,配置成功。

渲染Web頁面

在以前的示例中,咱們都是經過@RestController來處理請求,因此返回的內容爲json對象。那麼若是須要渲染html頁面的時候,要如何實現呢?

模板引擎

在動態HTML實現上Spring Boot依然能夠完美勝任,而且提供了多種模板引擎的默認配置支持,因此在推薦的模板引擎下,咱們能夠很快的上手開發動態網站。

Spring Boot提供了默認配置的模板引擎主要有如下幾種:

  • Thymeleaf
  • FreeMarker
  • Velocity
  • Groovy
  • Mustache
  • Spring Boot建議使用這些模板引擎,避免使用JSP,若必定要使用JSP將沒法實現Spring Boot的多種特性。

當使用上述模板引擎中的任何一個,它們默認的模板配置路徑爲:src/main/resources/templates。固然也能夠修改這個路徑,具體如何修改,可在後續模板引擎的配置屬性中查詢並修改。

Thymeleaf

Thymeleaf是一個XML/XHTML/HTML5模板引擎,可用於Web與非Web環境中的應用開發。它是一個開源的Java庫,基於Apache License 2.0許可,由Daniel Fernández建立,該做者仍是Java加密庫Jasypt的做者。

Thymeleaf提供了一個用於整合Spring MVC的可選模塊,在應用開發中,你可使用Thymeleaf來徹底代替JSP或其餘模板引擎,如Velocity、FreeMarker等。Thymeleaf的主要目標在於提供一種可被瀏覽器正確顯示的、格式良好的模板建立方式,所以也能夠用做靜態建模。你可使用它建立通過驗證的XML與HTML模板。相對於編寫邏輯或代碼,開發者只需將標籤屬性添加到模板中便可。接下來,這些標籤屬性就會在DOM(文檔對象模型)上執行預先制定好的邏輯。

示例模板:

<table>
  <thead>
    <tr>
      <th th:text="#{msgs.headers.name}">Name</td>
      <th th:text="#{msgs.headers.price}">Price</td>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod : ${allProducts}">
      <td th:text="${prod.name}">Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price,1,2)}">0.99</td>
    </tr>
  </tbody>
</table>

能夠看到Thymeleaf主要以屬性的方式加入到html標籤中,瀏覽器在解析html時,當檢查到沒有的屬性時候會忽略,因此Thymeleaf的模板能夠經過瀏覽器直接打開展示,這樣很是有利於先後端的分離。

在Spring Boot中使用Thymeleaf,只須要引入下面依賴,並在默認的模板路徑src/main/resources/templates下編寫模板文件便可完成。

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

在完成配置以後,舉一個簡單的例子,在前面工程的基礎上,舉一個簡單的示例來經過Thymeleaf渲染一個頁面。

定義Controller

@Controller
@RequestMapping("/view")
public class ViewController {

    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String index(Model model)
    {
        // 加入一個屬性,用來在模板中讀取
        model.addAttribute("host","http://www.dengcl.com");
        // return模板文件的名稱,對應src/main/resources/templates/index.html
        return "index";
    }

}

定義Thymeleaf模板頁面index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>
<h1 th:text="${host}">Hello World</h1>
</body>
</html>

注意: index.html文件在/src/main/resource/templates/目錄下。

如上頁面,直接打開html頁面展示Hello World,可是啓動程序後,訪問http://localhost:8080/view,則是展現Controller中host的值:·http://blog.didispace.com·,作到了不破壞HTML自身內容的數據邏輯分離。

Thymeleaf的默認參數配置

若有須要修改默認配置的時候,只需複製下面要修改的屬性到application.properties中,並修改爲須要的值,如修改模板文件的擴展名,修改默認的模板路徑等。

# Enable template caching.
spring.thymeleaf.cache=true 
# Check that the templates location exists.
spring.thymeleaf.check-template-location=true 
# Content-Type value.
spring.thymeleaf.content-type=text/html 
# Enable MVC Thymeleaf view resolution.
spring.thymeleaf.enabled=true 
# Template encoding.
spring.thymeleaf.encoding=UTF-8 
# Comma-separated list of view names that should be excluded from resolution.
spring.thymeleaf.excluded-view-names= 
# Template mode to be applied to templates. See also StandardTemplateModeHandlers.
spring.thymeleaf.mode=HTML5 
# Prefix that gets prepended to view names when building a URL.
spring.thymeleaf.prefix=classpath:/templates/ 
# Suffix that gets appended to view names when building a URL.

雖然spring boot支持這麼多模板引擎實現視圖渲染,可是全部的這些都是後端生成UI,在互聯網的應用中仍是建議設計成基於api的系統,後臺只提供api,前端開發html app。基於vue,react均可以,不過vue更輕量級。

Spring Boot中使用Swagger2構建強大的RESTful API文檔

因爲Spring Boot可以快速開發、便捷部署等特性,相信有很大一部分Spring Boot的用戶會用來構建RESTful API。而咱們構建RESTful API的目的一般都是因爲多終端的緣由,這些終端會共用不少底層業務邏輯,所以咱們會抽象出這樣一層來同時服務於多個移動端或者Web前端。

這樣一來,咱們的RESTful API就有可能要面對多個開發人員或多個開發團隊:IOS開發、Android開發或是Web開發等。爲了減小與其餘團隊平時開發期間的頻繁溝通成本,傳統作法咱們會建立一份RESTful API文檔來記錄全部接口細節,然而這樣的作法有如下幾個問題:

  1. 因爲接口衆多,而且細節複雜(須要考慮不一樣的HTTP請求類型、HTTP頭部信息、HTTP請求內容等),高質量地建立這份文檔自己就是件很是吃力的事,下游的抱怨聲不絕於耳。

  2. 隨着時間推移,不斷修改接口實現的時候都必須同步修改接口文檔,而文檔與代碼又處於兩個不一樣的媒介,除非有嚴格的管理機制,否則很容易致使不一致現象。

爲了解決上面這樣的問題,本文將介紹RESTful API的重磅好夥伴Swagger2,它能夠輕鬆的整合到Spring Boot中,並與Spring MVC程序配合組織出強大RESTful API文檔。它既能夠減小咱們建立文檔的工做量,同時說明內容又整合入實現代碼中,讓維護文檔和修改代碼整合爲一體,可讓咱們在修改代碼邏輯的同時方便的修改文檔說明。另外Swagger2也提供了強大的頁面測試功能來調試每一個RESTful API。具體效果以下圖所示:

image_1c8m567c611m6160lf0q68318eim.png-413.2kB

下面來具體介紹,若是在Spring Boot中使用Swagger2。首先,咱們須要一個Spring Boot實現的RESTful API工程,這裏使用前面的爲用戶構建RESTFul API的那個工程來持續開發。

添加依賴

pom.xml中加入Swagger2的依賴

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.2.2</version>
</dependency>

建立Swagger2配置類

在Springboot的入口函數類SbDemoApplication同級建立Swagger2的配置類SwaggerConfig。其代碼以下:

@Configuration //定義這是一個spring的配置類
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())//API的說明信息
                .select()
                //選定要生成API的接口或者類的父包
                .apis(RequestHandlerSelectors.basePackage("com.dengcl.sb_demo.controller"))
                .paths(PathSelectors.any()) //路徑規則,這裏是包中全部
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("XXXX項目接口說明文檔,使用Swagger2實現")
                .description("XXXX項目接口說明文檔的描述信息")
                .termsOfServiceUrl("http://127.0.0.1:8080")
                .contact("後臺研發團隊")
                .version("0.0.1-SNAPSHOT")
                .build();
    }

}

如上代碼所示,經過@Configuration註解,讓Spring來加載該類配置。再經過@EnableSwagger2註解來啓用Swagger2。

再經過createRestApi函數建立DocketBean以後,apiInfo()用來建立該Api的基本信息(這些基本信息會展示在文檔頁面中)。select()函數返回一個ApiSelectorBuilder實例用來控制哪些接口暴露給Swagger來展示,本例採用指定掃描的包路徑來定義,Swagger會掃描該包下全部Controller定義的API,併產生文檔內容(除了被@ApiIgnore指定的請求)

啓動spring boot應用,在瀏覽器輸入:localhost:8080/swagger-ui.html 就能夠查看接口文檔了,而且能夠在該文檔上執行API進行測試驗證。以下圖所示:

image_1c8m6s86l184c1eb3b93agb7p513.png-115.3kB

添加文檔內容

雖然在完成了上述配置後,已經能夠生產文檔內容,可是這樣的文檔主要針對請求自己,而描述主要來源於函數等命名產生,對用戶並不友好,咱們一般須要本身增長一些說明來豐富文檔內容。以下所示,咱們經過@ApiOperation註解來給API增長說明、經過@ApiImplicitParams@ApiImplicitParam註解來給參數增長說明。

@RestController
@RequestMapping(value="/users")     // 經過這裏配置使下面的映射都在/users下
public class UserController {
 
    // 建立線程安全的Map 
    static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());

    @ApiOperation(value="獲取用戶列表", notes="")
    @RequestMapping(value={""}, method=RequestMethod.GET)
    public List<User> getUserList() {
        List<User> r = new ArrayList<User>(users.values());
        return r;
    }

    @ApiOperation(value="建立用戶", notes="根據User對象建立用戶")
    @ApiImplicitParam(name = "user", value = "用戶詳細實體user", required = true, dataType = "User")
    @RequestMapping(value="", method=RequestMethod.POST)
    public String postUser(@RequestBody User user) {
        users.put(user.getId(), user);
        return "success";
    }

    @ApiOperation(value="獲取用戶詳細信息", notes="根據url的id來獲取用戶詳細信息")
    @ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long")
    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long id) {
        return users.get(id);
    }

    @ApiOperation(value="更新用戶詳細信息", notes="根據url的id來指定更新對象,並根據傳過來的user信息來更新用戶詳細信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long"),
            @ApiImplicitParam(name = "user", value = "用戶詳細實體user", required = true, dataType = "User")
    })
    @RequestMapping(value="/{id}", method=RequestMethod.PUT)
    public String putUser(@PathVariable Long id, @RequestBody User user) {
        User u = users.get(id);
        u.setName(user.getName());
        u.setAge(user.getAge());
        users.put(id, u);
        return "success";
    }

    @ApiOperation(value="刪除用戶", notes="根據url的id來指定刪除對象")
    @ApiImplicitParam(name = "id", value = "用戶ID", required = true, dataType = "Long")
    @RequestMapping(value="/{id}", method=RequestMethod.DELETE)
    public String deleteUser(@PathVariable Long id) {
        users.remove(id);
        return "success";
    }
 
}

完成上述代碼添加上,啓動Spring Boot程序,訪問:http://localhost:8080/swagger-ui.html
。就能看到前文所展現的RESTful API的頁面。咱們能夠再點開具體的API請求,以POST類型的/users請求爲例,可找到上述代碼中咱們配置的Notes信息以及參數user的描述信息,以下圖所示。

image_1c8m78cimk10151af0nh551bbd1g.png-111.7kB

API文檔訪問與調試

在上圖請求的頁面中,咱們看到user的Value是個輸入框?是的,Swagger除了查看接口功能外,還提供了調試測試功能,咱們能夠點擊上圖中右側的Model Schema(黃色區域:它指明瞭User的數據結構),此時Value中就有了user對象的模板,咱們只須要稍適修改,點擊下方「Try it out!」按鈕,便可完成了一次請求調用!

此時,你也能夠經過幾個GET請求來驗證以前的POST請求是否正確。

相比爲這些接口編寫文檔的工做,咱們增長的配置內容是很是少並且精簡的,對於原有代碼的侵入也在忍受範圍以內。所以,在構建RESTful API的同時,加入swagger來對API文檔進行管理,是個不錯的選擇。

統一異常處理

咱們在作Web應用的時候,請求處理過程當中發生錯誤是很是常見的狀況。Spring Boot提供了一個默認的映射:/error,當處理中拋出異常以後,會轉到該請求中處理,而且該請求有一個全局的錯誤頁面用來展現異常內容。

選擇上一章節實現過的Web應用爲基礎,啓動該應用,訪問一個不存在的URL,或是修改處理內容,直接拋出異常,如:

@RestController
public class HelloWorld {

    @RequestMapping("/hello")
    public String sayHello(){
        return "Hello World!";
    }

    @RequestMapping("/happenError")
    public String happenError() throws Exception {
        throw new Exception("發生錯誤");
    }
}

此時啓動spring boot,訪問/happenError,能夠看到相似下面的報錯頁面,該頁面就是Spring Boot提供的默認error映射頁面。

image_1c8m7gc8gj7ms5qo8opqjcj01t.png-76.3kB

統一異常處理

雖然,Spring Boot中實現了默認的error映射,可是在實際應用中,上面你的錯誤頁面對用戶來講並不夠友好,咱們一般須要去實現咱們本身的異常提示。

下面咱們在上一章的應用中來進行統一異常處理的改造。

建立全局異常處理類:經過使用@ControllerAdvice定義統一的異常處理類,而不是在每一個Controller中逐個定義。@ExceptionHandler用來定義函數針對的異常類型,最後將Exception對象和請求URL映射到error.html

@ControllerAdvice
class GlobalExceptionHandler {

    public static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }

}

實現error.html頁面展現:在templates目錄下建立error.html,將請求的URLException對象的message輸出。

<!DOCTYPE html>
<html>
<head lang="en" >
    <meta charset="UTF-8" />
    <title>統一異常處理</title>
</head>
<body>
<h1>Error Handler</h1>
<div th:text="${url}"></div>
<div th:text="${exception.message}"></div>
</body>
</html>

啓動該應用,訪問:http://localhost:8080/happenError,能夠看到以下錯誤提示頁面。

image_1c8m8onm2vkg1ih9v6tj4k10fh2a.png-51.1kB

經過實現上述內容以後,咱們只須要在Controller中拋出Exception,固然咱們可能會有多種不一樣的Exception。而後在@ControllerAdvice類中,根據拋出的具體Exception類型匹配@ExceptionHandler中配置的異常類型來匹配錯誤映射和處理。

返回JSON格式

在上述例子中,經過@ControllerAdvice統必定義不一樣Exception映射到不一樣錯誤處理頁面。而當咱們要實現RESTful API時,返回的錯誤是JSON格式的數據,而不是HTML頁面,這時候咱們也能輕鬆支持。

本質上,只需在@ExceptionHandler以後加入@ResponseBody,就能讓處理函數return的內容轉換爲JSON格式。

下面以一個具體示例來實現返回JSON格式的異常處理。

  • 建立統一的JSON返回對象,code:消息類型,message:消息內容,url:請求的url,data:請求返回的數據
public class ErrorInfo<T> {

    public static final Integer OK = 0;
    public static final Integer ERROR = 100;

    private Integer code;
    private String message;
    private String url;
    private T data;

    // 省略getter和setter

}
  • 修改統一異常處理類
@ControllerAdvice
class GlobalExceptionHandler {

    public static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = Exception.class)
    @ResponseBody //異常時響應JSON
    public ErrorInfo defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        ErrorInfo errorInfo = new ErrorInfo();
        errorInfo.setUrl(req.getRequestURL().toString());

        //NoHandlerFoundException對應的是404異常
        if(e instanceof NoHandlerFoundException){
            errorInfo.setCode(404);
            errorInfo.setMessage("頁面找不到");
        }else{
            errorInfo.setCode(500);
            errorInfo.setMessage(e.getMessage());
        }
        return errorInfo;
    }

}
  • spring boot的配置文件中增長如下配置
#出現錯誤時, 直接拋出異常
spring.mvc.throw-exception-if-no-handler-found=true
#不要爲咱們工程中的資源文件創建映射,
spring.resources.add-mappings=false

說明: spring boot中發生404,默認處理是servlet容器的404處理方案,而不是拋出異常,須要增長上面的配置後才能處理404.
設置spring.resources.add-mappings=false後,客戶端不能直接訪問工程中的資源文件了。

  • 啓動該應用,訪問:http://localhost:8080/happenError,能夠看到以下內容。

image_1c8m9f57rl5v2ltok7gnl96f2n.png-177.4kB

若是咱們訪問一個不存在映射地址時,以下:

image_1c8m9i1ahbkm5hqtdtiqjc0e9.png-195.4kB

至此,已完成在Spring Boot中建立統一的異常處理,實際實現仍是依靠Spring MVC的註解,更多更深刻的使用可參考Spring MVC的文檔。


Spring Boot日誌

Spring Boot在全部內部日誌中默認使用Commons Logging,可是默認配置也提供了對經常使用日誌的支持,如:Java Util LoggingLog4J, Log4J2Logback。每種Logger均可以經過配置使用控制檯或者文件輸出日誌內容。

Spring Boot日誌管理

格式化日誌

默認的日誌輸出以下:

2018-03-16 11:01:19.728  INFO 10600 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed

輸出內容元素具體以下:

  • 時間日期 — 精確到毫秒
  • 日誌級別 — ERROR, WARN, INFO, DEBUG or TRACE
  • 進程ID
  • 分隔符 — --- 標識實際日誌的開始
  • 線程名 — 方括號括起來(可能會截斷控制檯輸出)
  • Logger名 — 一般使用源代碼的類名
  • 日誌內容

控制檯輸出

在Spring Boot中默認配置了ERRORWARNINFO級別的日誌輸出到控制檯。

咱們能夠經過兩種方式切換至DEBUG級別:

  • 在運行命令後加入--debug標誌,如:java -jar myapp.jar --debug
  • application.properties中配置debug=true,該屬性置爲true的時候,核心Logger(包含嵌入式容器、hibernate、spring)會輸出更多內容,可是你本身應用的日誌並不會輸出爲DEBUG級別。

多彩輸出

若是你的終端支持ANSI,設置彩色輸出會讓日誌更具可讀性。經過在application.properties中設置spring.output.ansi.enabled參數來支持。

  • NEVER:禁用ANSI-colored輸出
  • DETECT:會檢查終端是否支持ANSI,是的話就採用彩色輸出(默認項)
  • ALWAYS:老是使用ANSI-colored格式輸出,若終端不支持的時候,會有不少干擾信息,不推薦使用

文件輸出

Spring Boot默認配置只會輸出到控制檯,並不會記錄到文件中,可是咱們一般生產環境使用時都須要以文件方式記錄。

若要增長文件輸出,須要在application.properties中配置logging.filelogging.path屬性。

  • logging.file,設置文件,能夠是絕對路徑,也能夠是相對路徑。如:logging.file=my.log
  • logging.path,設置目錄,會在該目錄下建立spring.log文件,並寫入日誌內容,如:logging.path=/var/log

日誌文件會在10Mb大小的時候被截斷,產生新的日誌文件,默認級別爲:ERROR、WARN、INFO

級別控制

在Spring Boot中只須要在application.properties中進行配置完成日誌記錄的級別控制。

配置格式:logging.level.*=LEVEL

  • logging.level:日誌級別控制前綴,*爲包名或Logger名
  • LEVEL:選項TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF

舉例:

  • logging.level.com.dengcl=DEBUGcom.dengcl包下全部class以DEBUG級別輸出
  • logging.level.root=WARN :root日誌以WARN級別輸出

自定義日誌配置

因爲日誌服務通常都在ApplicationContext建立前就初始化了,它並非必須經過Spring的配置文件控制。所以經過系統屬性和傳統的Spring Boot外部配置文件依然能夠很好的支持日誌控制和管理。

根據不一樣的日誌系統,你能夠按以下規則組織配置文件名,就能被正確加載:
|日誌系統|配置文件名|
|---|----|
|Logback|logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy|
|Log4j|log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml|
|Log4j2|log4j2-spring.xml, log4j2.xml|
|JDK (Java Util Logging)|logging.properties|

Spring Boot官方推薦優先使用帶有-spring的文件名做爲你的日誌配置(如使用logback-spring.xml,而不是logback.xml

自定義輸出格式

在Spring Boot中能夠經過在application.properties配置以下參數控制輸出格式:

  • logging.pattern.console:定義輸出到控制檯的樣式(不支持JDK Logger)
  • logging.pattern.file:定義輸出到文件的樣式(不支持JDK Logger)

Spring Boot中使用AOP統一處理Web請求日誌

AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是Spring框架中的一個重要內容,它經過對既有程序定義一個切入點,而後在其先後切入不一樣的執行內容,好比常見的有:打開數據庫鏈接/關閉數據庫鏈接、打開事務/關閉事務、記錄日誌等。基於AOP不會破壞原來程序邏輯,所以它能夠很好的對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。

下面主要講兩個內容,一個是如何在Spring Boot中引入Aop功能,二是如何使用Aop作切面去統一處理Web請求的日誌。

準備工做

由於須要對web請求作切面來記錄日誌,因此先引入web模塊,並建立一個簡單的hello請求的處理。

pom.xml中引入web模塊

引入AOP依賴

在Spring Boot中引入AOP就跟引入其餘模塊同樣,很是簡單,只須要在pom.xml中加入以下依賴:

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

在完成了引入AOP依賴包後,通常來講並不須要去作其餘配置。也許在Spring中使用過註解配置方式的人會問是否須要在程序主類中增長@EnableAspectJAutoProxy來啓用,實際並不須要。

能夠看下面關於AOP的默認配置屬性,其中spring.aop.auto屬性默認是開啓的,也就是說只要引入了AOP依賴後,默認已經增長了@EnableAspectJAutoProxy

# AOP
spring.aop.auto=true # Add @EnableAspectJAutoProxy.
spring.aop.proxy-target-class=false # Whether subclass-based (CGLIB) proxies are to be created (true) as
 opposed to standard Java interface-based proxies (false).

而當咱們須要使用CGLIB來實現AOP的時候,須要配置spring.aop.proxy-target-class=true,否則默認使用的是標準Java的實現AOP。

實現Web層的日誌切面

實現AOP的切面主要有如下幾個要素:

  • 使用@Aspect註解將一個java類定義爲切面類
  • 使用@Pointcut定義一個切入點,能夠是一個規則表達式,好比下例中某個package下的全部函數,也能夠是一個註解等。
  • 根據須要在切入點不一樣位置的切入內容
    • 使用@Before在切入點開始處切入內容
    • 使用@After在切入點結尾處切入內容
    • 使用@AfterReturning在切入點return內容以後切入內容(能夠用來對處理返回值作一些加工處理)
    • 使用@Around在切入點先後切入內容,並本身控制什麼時候執行切入點自身的內容
    • 使用@AfterThrowing用來處理當切入內容部分拋出異常以後的處理邏輯

切面類代碼以下:

@Aspect
@Component
public class WebLogAspect {

    private Logger logger = Logger.getLogger(this.getClass());

    @Pointcut("execution(public * com.dengcl.sb_demo.controller.*.*(..))")
    public void pt(){}

    @Before("pt()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到請求,記錄請求內容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 記錄下請求內容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }

    @AfterReturning(returning = "ret", pointcut = "pt()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 處理完請求,返回內容
        logger.info("RESPONSE : " + ret);
    }

}

能夠看上面的例子,經過·@Pointcut·定義的切入點爲com.dengcl.sb_demo.controller包下的全部函數(對web層全部請求處理作切入點),而後經過@Before實現,對請求內容的日誌記錄(本文只是說明過程,能夠根據須要調整內容),最後經過@AfterReturning記錄請求返回的對象。

經過運行程序並訪問:http://localhost:8080/users/,能夠得到下面的日誌輸出

2018-03-16 11:01:32.662  INFO 10600 --- [nio-8080-exec-1] com.dengcl.sb_demo.advice.WebLogAspect   : URL : http://localhost:8080/users
2018-03-16 11:01:32.662  INFO 10600 --- [nio-8080-exec-1] com.dengcl.sb_demo.advice.WebLogAspect   : HTTP_METHOD : GET
2018-03-16 11:01:32.663  INFO 10600 --- [nio-8080-exec-1] com.dengcl.sb_demo.advice.WebLogAspect   : IP : 0:0:0:0:0:0:0:1
2018-03-16 11:01:32.663  INFO 10600 --- [nio-8080-exec-1] com.dengcl.sb_demo.advice.WebLogAspect   : CLASS_METHOD : com.dengcl.sb_demo.controller.UserController.getUserList
2018-03-16 11:01:32.665  INFO 10600 --- [nio-8080-exec-1] com.dengcl.sb_demo.advice.WebLogAspect   : ARGS : []
2018-03-16 11:01:32.675  INFO 10600 --- [nio-8080-exec-1] com.dengcl.sb_demo.advice.WebLogAspect   : RESPONSE : []

優化:AOP切面中的同步問題

在WebLogAspect切面中,分別經過doBeforedoAfterReturning兩個獨立函數實現了切點頭部和切點返回後執行的內容,若咱們想統計請求的處理時間,就須要在doBefore處記錄時間,並在doAfterReturning處經過當前時間與開始處記錄的時間計算獲得請求處理的消耗時間。

那麼咱們是否能夠在WebLogAspect切面中定義一個成員變量來給doBeforedoAfterReturning一塊兒訪問呢?是否會有同步問題呢?

的確,直接在這裏定義基本類型會有同步問題,因此咱們能夠引入ThreadLocal對象,像下面這樣進行記錄:

@Aspect
@Component
public class WebLogAspect {

    private Logger logger = Logger.getLogger(getClass());

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Pointcut("execution(public * com.dengcl.sb_demo.controller.*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());

        // 省略日誌記錄內容
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 處理完請求,返回內容
        logger.info("RESPONSE : " + ret);
        logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
    }


}

優化:AOP切面的優先級

因爲經過AOP實現,程序獲得了很好的解耦,可是也會帶來一些問題,好比:咱們可能會對Web層作多個切面,校驗用戶,校驗頭信息等等,這個時候常常會碰到切面的處理順序問題。

因此,咱們須要定義每一個切面的優先級,咱們須要@Order(i)註解來標識切面的優先級。i的值越小,優先級越高。假設咱們還有一個切面是CheckNameAspect用來校驗name必須爲didi,咱們爲其設置@Order(10),而上文中WebLogAspect設置爲@Order(5),因此WebLogAspect有更高的優先級,這個時候執行順序是這樣的:

  • @Before中優先執行@Order(5)的內容,再執行@Order(10)的內容
  • @After@AfterReturning中優先執行@Order(10)的內容,再執行@Order(5)的內容

因此咱們能夠這樣子總結:

  • 在切入點前的操做,按order的值由小到大執行
  • 在切入點後的操做,按order的值由大到小執行

Spring boot整合mybatis操做數據庫

添加依賴

pom.xml中引入依賴

引入鏈接mysql的必要依賴mysql-connector-java
引入整合MyBatis的核心依賴mybatis-spring-boot-starter

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.45</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>

配置數據源信息

application.properties中配置mysql的鏈接配置

spring.datasource.url=jdbc:mysql://localhost:3306/users
spring.datasource.username=root
spring.datasource.password=12345678
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

簡單且簡潔的的完成了基本配置,下面看看如何在這個基礎下輕鬆方便的使用MyBatis訪問數據庫。

使用MyBatis

在Mysql中建立User表,包含id(BIGINT)、name(INT)、age(VARCHAR)字段。同時,建立映射對象User,這裏再也不列出建表和映射對象的代碼。

建立User映射的操做UserMapper.java,爲了後續單元測試驗證,實現插入和查詢操做。

@Mapper
public interface UserMapper {

    @Select("SELECT * FROM USERS WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

    @Insert("INSERT INTO USERS(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);
}

@Mapper定義本接口是一個mybatis的mapper接口類。

建立單元測試

  • 測試邏輯:插入一條name=AAA,age=20的記錄,而後根據name=AAA查詢,並判斷age是否爲20
  • 測試結束回滾數據,保證測試單元每次運行的數據環境獨立
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper mapper;


    @Test
    @Transactional
    @Rollback
    public void findByName() throws Exception {
        mapper.insert("AAA", 20);
        User u = mapper.findByName("AAA");
        Assert.assertEquals(20, u.getAge().intValue());
    }

}

使用xml實現mapper

前面的示例是經過註解的方式來實現mybatis的mapper,可是在複雜的應用場景下,基於xml的mapper更加靈活,更方便維護。

修改UserMapper,去掉註解

@Mapper
public interface UserMapper {

//    @Select("SELECT * FROM USERS WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

//    @Insert("INSERT INTO USERS(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);
}

創建UserMapper.xml,實現mybatis映射

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dengcl.sb_demo.mapper.UserMapper">
    <select id="findByName" resultType="com.dengcl.sb_demo.pojo.User">
        SELECT * FROM USERS WHERE NAME = #{name}
    </select>

    <insert id="insert" >
        INSERT INTO USERS(NAME, AGE) VALUES(#{name}, #{age})
    </insert>
</mapper>

特別重要:配置mybatis的mapper文件的所在位置

application.properties文件中增長以下配置:

mybatis.mapper-locations=classpath:/com/dengcl/sb_demo/mapper/*.xml

事務

在Spring Boot中,當咱們使用了spring-boot-starter-jdbc依賴的時候,框架會自動默認分別注入DataSourceTransactionManagerJpaTransactionManager。因此咱們不須要任何額外配置就能夠用@Transactional註解進行事務的使用。

注意:在上面的示例中咱們雖然未使用spring-boot-starter-jdbc依賴,但也能夠直接使用事務,緣由是mybatis-spring-boot-starter中已經包含了此依賴

例如:

@Test
    @Transactional
    public void batchAdd(){
        mapper.insert("aaa",12);
        mapper.insert("aaaa",13);
        mapper.insert("aaaaa",14);
        mapper.insert("aaaaaa",15);
        mapper.insert("aaaaaaa",16);
    }

該方法中的多個數據操做就在同一個事務中。

這裏是經過單元測試演示瞭如何使用@Transactional註解來聲明一個方法須要被事務管理,一般咱們單元測試爲了保證每一個測試之間的數據獨立,會使用@Rollback註解讓每一個單元測試都能在結束時回滾。而真正在開發業務邏輯時,咱們一般在service層接口中使用@Transactional來對各個業務邏輯進行事務管理的配置,例如:

public interface UserService {
    
    @Transactional
    User login(String name, String password);
    
}

事務詳解

上面的例子中咱們使用了默認的事務配置,能夠知足一些基本的事務需求,可是當咱們項目較大較複雜時(好比,有多個數據源等),這時候須要在聲明事務時,指定不一樣的事務管理器。在聲明事務時,只須要經過value屬性指定配置的事務管理器名便可,例如:@Transactional(value="transactionManagerPrimary")

除了指定不一樣的事務管理器以後,還能對事務進行隔離級別和傳播行爲的控制,下面分別詳細解釋:

 隔離級別

隔離級別是指若干個併發的事務之間的隔離程度,與咱們開發時候主要相關的場景包括:髒讀取、重複讀、幻讀。

咱們能夠看org.springframework.transaction.annotation.Isolation枚舉類中定義了五個表示隔離級別的值:

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);
}
  • DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,一般這值就是READ_COMMITTED
  • READ_UNCOMMITTED:該隔離級別表示一個事務能夠讀取另外一個事務修改但尚未提交的數據。該級別不能防止髒讀和不可重複讀,所以不多使用該隔離級別。
  • READ_COMMITTED:該隔離級別表示一個事務只能讀取另外一個事務已經提交的數據。該級別能夠防止髒讀,這也是大多數狀況下的推薦值。
  • REPEATABLE_READ:該隔離級別表示一個事務在整個過程當中能夠屢次重複執行某個查詢,而且每次返回的記錄都相同。即便在屢次查詢之間有新增的數據知足該查詢,這些新增的記錄也會被忽略。該級別能夠防止髒讀和不可重複讀。
  • SERIALIZABLE:全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。

指定方法:經過使用isolation屬性設置事務的隔離級別,例如:

@Transactional(isolation = Isolation.DEFAULT)

傳播行爲

所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。

咱們能夠看org.springframework.transaction.annotation.Propagation枚舉類中定義了6個表示傳播行爲的枚舉值:

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);
}
  • REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。
  • SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
  • MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
  • REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
  • NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
  • NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
  • NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於REQUIRED

指定方法:經過使用propagation屬性設置事務的傳播行爲,例如:

@Transactional(propagation = Propagation.REQUIRED)

監控和管理生產環境

相關文章
相關標籤/搜索