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)

监控和管理生产环境

相关文章
相关标签/搜索