SpringBoot_第二天

SpringBoot_第二天

學習目標

Mybatis整合&數據訪問

使用SpringBoot開發企業項目時,持久層數據訪問是前端頁面數據展示的基礎,SpringBoot支持市面上常見的關系庫產品(Oracle,Mysql,SqlServer,DB2等)對應的相關持久層框架,當然除了對于關系庫訪問的支持,也支持當下眾多的非關系庫(Redis,Solr,MongoDB等)數據訪問操作,這里主要介紹SpringBoot集成Mybatis并實現持久層數據基本增刪改查操作。

SpringBoot 整合Mybatis

環境整合配置

Idea 下創建Maven 普通工程 springboot_mybatis

pom.xml 添加核心依賴

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<maven.compiler.source>1.8</maven.compiler.source>

<maven.compiler.target>1.8</maven.compiler.target>

</properties>

?

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.2.2.RELEASE</version>

</parent>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<!--

mybatis 集成

-->

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>2.1.1</version>

</dependency>

<!-- springboot分頁插件 -->

<dependency>

<groupId>com.github.pagehelper</groupId>

<artifactId>pagehelper-spring-boot-starter</artifactId>

<version>1.2.13</version>

</dependency>

?

<!--mysql 驅動-->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>


</dependency>

<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->

<dependency>

<groupId>com.mchange</groupId>

<artifactId>c3p0</artifactId>

<version>0.9.5.5</version>

</dependency>

</dependencies>

?

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

application.yml 整合配置

## 端口號

server:

? port: 9999

?

## 數據源配置

spring:

? datasource:

?? type: com.mchange.v2.c3p0.ComboPooledDataSource

?? driver-class-name: com.mysql.cj.jdbc.Driver

?? url: jdbc:mysql://127.0.0.1:3306/springboot_mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8

?? username: root

?? password: root

?

## mybatis 配置

mybatis:

? mapper-locations: classpath:/mappers/*.xml

? type-aliases-package: com.xxxx.springboot.vo

? configuration:

## 下劃線轉駝峰配置

?? map-underscore-to-camel-case: true

?

## pageHelper

pagehelper:

? helper-dialect: mysql


#顯示dao 執行sql語句

logging:

? level:

?? com:

? ?? xxxx:

? ? ?? springboot:

? ? ? ?? dao: debug

源代碼添加

Dao層接口方法定義

com.xxxx.springboot.dao 包下創建UserDao.java 接口聲明查詢方法

packagecom.xxxx.springboot.dao;

?

importcom.xxxx.springboot.vo.User;

?

publicinterfaceUserMapper{

? ? // 根據用戶名查詢用戶記錄

UserqueryUserByUserName(StringuserName);

}

SQL映射文件添加

resources/mappers 目錄下添加UserMapper.xml 配置查詢statetment

<?xmlversion="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mappernamespace="com.xxxx.springboot.dao.UserMapper">

<selectid="queryUserByUserName"parameterType="string"resultType="com.xxxx.springboot.vo.User">

? ? ?? select

? ? ?? id,user_name,user_pwd

? ? ?? from t_user

? ? ?? where user_name=#{userName}

</select>

</mapper>

添加service 、controller 對應代碼

UserService.java

@Service

publicclassUserService{

@Autowired

privateUserMapperuserMapper;

?

publicUserqueryUserByUserName(StringuserName){

returnuserMapper.queryUserByUserName(userName);

?? }

}

UserController.java

@RestController

publicclassUserController{

?

@Resource

privateUserServiceuserService;

?

?

@GetMapping("user/{userName}")

publicUserqueryUserByUserName(@PathVariableStringuserName){

returnuserService.queryUserByUserName(userName);

?? }

}

添加應用啟動入口

@SpringBootApplication

@MapperScan("com.xxxx.springboot.dao")

publicclassStarter{

?

publicstaticvoidmain(String[]args) {

SpringApplication.run(Starter.class);

?? }

}

啟動測試

運行Starter? main方法,啟動應用瀏覽器測試查詢

后端日志打印效果:

SpringBoot數據訪問操作

完成SpringBoot 與Mybatis 集成后,接下來以用戶表為例實現一套用戶模塊基本數據維護。

接口方法 & Sql映射文件

UserDao 接口方法定義

UserDao 接口添加數據訪問基本方法

publicinterfaceUserMapper{

?

publicUserqueryById(Integerid);

?

UserqueryUserByUserName(StringuserName);

?

publicintsave(Useruser);

?

publicintupdate(Useruser);

?

publicList<User>selectByParams(UserQueryuserQuery);

?

}

UserMapper.xml 映射文件配置

<?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.xxxx.springboot.dao.UserMapper">

? ? <select id="queryById" parameterType="int" resultType="com.xxxx.springboot.vo.User">

? ? ? ? select *

? ? ? ? from t_user

? ? ? ? where id = #{id,jdbcType=INTEGER}

? ? </select>

? ? <select id="queryUserByUserName" parameterType="string" resultType="com.xxxx.springboot.vo.User">

? ? ? ? select *

? ? ? ? from t_user

? ? ? ? where user_name=#{userName}

? ? </select>

? ? <insert id="save" parameterType="com.xxxx.springboot.vo.User" useGeneratedKeys="true" keyProperty="id">

? ? ? ? insert into t_user(id,user_name,user_pwd) values(#{id},#{userName},#{userPwd})

? ? </insert>

? ? <update id="update" parameterType="com.xxxx.springboot.vo.User">

? ? ? ? update t_user set user_name =#{userName},user_pwd=#{userPwd}

? ? ? ? where id = #{id}

? ? </update>

? ? <select id="selectByParams" parameterType="com.xxxx.springboot.query.UserQuery" resultType="com.xxxx.springboot.vo.User">

? ? ? ? select *

? ? ? ? from t_user

? ? ? ? <where>

? ? ? ? ? ? <if test="null !=userName and userName !=''">

? ? ? ? ? ? ? ? and user_name like concat('%',#{userName},'%')

? ? ? ? ? ? </if>

? ? ? ? </where>

? ? </select>

</mapper>

UserService.java方法實現

public User queryUserByUserName(String userName){

? ? return userMapper.queryUserByUserName(userName);

}

public User queryUserByUserId(Integer userId){

? ? return userMapper.queryById(userId);

}

public void saveUser(User user) {

? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用戶名不能為空!");

? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用戶密碼不能為空!");

? ? User temp = userMapper.queryUserByUserName(user.getUserName());

? ? AssertUtil.isTrue(null != temp, "該用戶已存在!");

? ? AssertUtil.isTrue(userMapper.save(user)<1,"用戶記錄添加失敗!");

}

public void updateUser(User user) {

? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用戶名不能為空!");

? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用戶密碼不能為空!");

? ? User temp = userMapper.queryUserByUserName(user.getUserName());

? ? AssertUtil.isTrue(null != temp && !(temp.getId().equals(user.getId())), "該用戶已存在!");

? ? AssertUtil.isTrue(userMapper.update(user)<1,"用戶記錄添加失敗!");

}

public? void deleteUser(Integer id){

? ? AssertUtil.isTrue(null == id || null ==userMapper.queryById(id),"待刪除記錄不存在!");

? ? AssertUtil.isTrue(userMapper.delete(id)<1,"用戶刪除失敗!");

}

public PageInfo<User> queryUserByParams(UserQuery userQuery){

? ? PageHelper.startPage(userQuery.getPageNum(),userQuery.getPageSize());

? ? return new PageInfo<User>(userMapper.selectByParams(userQuery));

}

UserController.java 接口方法

@GetMapping("user/{userId}")

public User queryUserByUserId(@PathVariable? Integer userId){

? ? return userService.queryUserByUserId(userId);

}

@GetMapping("user/list")

public PageInfo<User> list(UserQuery userQuery){

? ? return userService.queryUserByParams(userQuery);

}

@PutMapping("user")

public ResultInfo saveUser(User user){

? ? ResultInfo resultInfo=new ResultInfo();

? ? try {

? ? ? ? userService.saveUser(user);

? ? } catch (ParamsException e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(e.getCode());

? ? ? ? resultInfo.setMsg(e.getMsg());

? ? }catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("記錄添加失敗!");

? ? }

? ? return resultInfo;

}

@PostMapping("user")

public ResultInfo updateUser(User user){

? ? ResultInfo resultInfo=new ResultInfo();

? ? try {

? ? ? ? userService.updateUser(user);

? ? } catch (ParamsException e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(e.getCode());

? ? ? ? resultInfo.setMsg(e.getMsg());

? ? }catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("記錄更新失敗!");

? ? }

? ? return resultInfo;

}

@DeleteMapping("user/{userId}")

public ResultInfo deleteUser(@PathVariable? Integer? userId){

? ? ResultInfo resultInfo=new ResultInfo();

? ? try {

? ? ? ? userService.deleteUser(userId);

? ? } catch (ParamsException e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(e.getCode());

? ? ? ? resultInfo.setMsg(e.getMsg());

? ? }catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("記錄刪除失敗!");

? ? }

? ? return resultInfo;

}

PostMan 接口測試工具下載與使用

在企業web 應用開發中,對服務器端接口進行測試,通常借助接口測試工具,這里使用Postman 接口測試工具來對后臺restful接口進行測試,Postman 工具下載地址: https://www.getpostman.com/apps 選中對應平臺下載即可。

下載安裝后,啟動Postman 根據后臺接口地址發送響應請求即可對接口進行測試。

API 文檔構建工具-Swagger2

由于Spring Boot能夠快速開發、便捷部署等特性,通常在使用Spring Boot構建Restful 接口應用時考慮到多終端的原因,這些終端會共用很多底層業務邏輯,因此我們會抽象出這樣一層來同時服務于多個移動端或者Web前端。對于不同的終端公用一套接口API時對于聯調測試時就需要知道后端提供的接口Api 列表文檔,對于服務端開發人員來說就需要編寫接口文檔,描述接口調用地址參數結果等,這里借助第三方構建工具Swagger2來實現Api文檔生成功能。

環境整合配置

pom.xml 依賴添加

<dependency>

? <groupId>io.springfox</groupId>

? <artifactId>springfox-swagger2</artifactId>

? <version>2.9.2</version>

</dependency>

<dependency>

? <groupId>io.springfox</groupId>

? <artifactId>springfox-swagger-ui</artifactId>

? <version>2.9.2</version>

</dependency>

配置類添加

@Configuration

@EnableSwagger2

public class Swagger2 {

? ? @Bean

? ? public Docket createRestApi() {

? ? ? ? return new Docket(DocumentationType.SWAGGER_2)

? ? ? ? ? ? ? ? .apiInfo(apiInfo())

? ? ? ? ? ? ? ? .select()

? ? ? ? ? ? ? ? .apis(RequestHandlerSelectors.basePackage("com.xxxx.springboot.controller"))

? ? ? ? ? ? ? ? .paths(PathSelectors.any())

? ? ? ? ? ? ? ? .build();

? ? }

? ? private ApiInfo apiInfo() {

? ? ? ? return new ApiInfoBuilder()

? ? ? ? ? ? ? ? .title("用戶管理接口API文檔參考")

? ? ? ? ? ? ? ? .version("1.0")

? ? ? ? ? ? ? ? .build();

? ? }

}

Swagger2 常用注解說明

@Api

@Api:用在請求的類上,說明該類的作用

? ? tags="說明該類的作用"

@Api(tags="APP用戶注冊Controller")

@ApiOperation

@ApiOperation:"用在請求的方法上,說明方法的作用"

? ? value="說明方法的作用"

? ? notes="方法的備注說明"

@ApiOperation(value="用戶注冊",notes="手機號、密碼都是必輸項,年齡隨邊填,但必須是數字")

@ApiImplicitParams

@ApiImplicitParams:用在請求的方法上,包含一組參數說明

? ? @ApiImplicitParam:用在 @ApiImplicitParams 注解中,指定一個請求參數的配置信息? ? ?

? ? ? ? name:參數名

? ? ? ? value:參數的漢字說明、解釋

? ? ? ? required:參數是否必須傳

? ? ? ? paramType:參數放在哪個地方

? ? ? ? ? ? · header --> 請求參數的獲取:@RequestHeader

? ? ? ? ? ? · query --> 請求參數的獲取:@RequestParam

? ? ? ? ? ? · path(用于restful接口)--> 請求參數的獲取:@PathVariable

? ? ? ? ? ? · body(不常用)

? ? ? ? ? ? · form(不常用)? ?

? ? ? ? dataType:參數類型,默認String,其它值dataType="Integer"? ? ?

? ? ? ? defaultValue:參數的默認值

@ApiImplicitParams({

? ? @ApiImplicitParam(name="mobile",value="手機號",required=true,paramType="form"),

? ? @ApiImplicitParam(name="password",value="密碼",required=true,paramType="form"),

? ? @ApiImplicitParam(name="age",value="年齡",required=true,paramType="form",dataType="Integer")

})

@ApiResponses

@ApiResponses:用于請求的方法上,表示一組響應

? ? @ApiResponse:用在@ApiResponses中,一般用于表達一個錯誤的響應信息

? ? ? ? code:數字,例如400

? ? ? ? message:信息,例如"請求參數沒填好"

? ? ? ? response:拋出異常的類

@ApiOperation(value = "select請求",notes = "多個參數,多種的查詢參數類型")

@ApiResponses({

? ? @ApiResponse(code=400,message="請求參數沒填好"),

? ? @ApiResponse(code=404,message="請求路徑沒有或頁面跳轉路徑不對")

})

@ApiModel

@ApiModel:用于響應類上,表示一個返回響應數據的信息

? ? ? ? ? ? (這種一般用在post創建的時候,使用@RequestBody這樣的場景,

? ? ? ? ? ? 請求參數無法使用@ApiImplicitParam注解進行描述的時候)

? ? @ApiModelProperty:用在屬性上,描述響應類的屬性

@ApiModel(description= "返回響應數據")

public class RestMessage implements Serializable{

? ? @ApiModelProperty(value = "是否成功")

? ? private boolean success=true;

? ? @ApiModelProperty(value = "返回對象")

? ? private Object data;

? ? @ApiModelProperty(value = "錯誤編號")

? ? private Integer errCode;

? ? @ApiModelProperty(value = "錯誤信息")

? ? private String message;

? ? /* getter/setter */

}

用戶模塊注解配置

UserController.java 接口方法注解使用

@GetMapping("user/uname/{userName}")

@ApiOperation(value = "根據用戶名查詢用戶記錄")

@ApiImplicitParam(name = "userName",value = "查詢參數",required = true,paramType = "path")

public User queryUserByUserName(@PathVariable String userName){

? ? return userService.queryUserByUserName(userName);

}

@ApiOperation(value = "根據用戶id查詢用戶記錄")

@ApiImplicitParam(name = "userId",value = "查詢參數",required = true,paramType = "path")

@GetMapping("user/{userId}")

public User queryUserByUserId(@PathVariable? Integer userId, HttpServletRequest request){

? ? return userService.queryUserByUserId(userId);

}

@GetMapping("user/list")

@ApiOperation(value = "多條件查詢用戶列表記錄")

public PageInfo<User> list(UserQuery userQuery){

? ? return userService.queryUserByParams(userQuery);

}

@PutMapping("user")

@ApiOperation(value = "用戶添加")

@ApiImplicitParam(name = "user",value = "用戶實體類",dataType = "User")

public ResultInfo saveUser(@RequestBody? User user){

? ? ResultInfo resultInfo=new ResultInfo();

? ? try {

? ? ? ? userService.saveUser(user);

? ? } catch (ParamsException e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(e.getCode());

? ? ? ? resultInfo.setMsg(e.getMsg());

? ? }catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("記錄添加失敗!");

? ? }

? ? return resultInfo;

}

@PostMapping("user")

@ApiOperation(value = "用戶更新")

@ApiImplicitParam(name = "user",value = "用戶實體類",dataType = "User")

public ResultInfo updateUser(@RequestBody? User user){

? ? ResultInfo resultInfo=new ResultInfo();

? ? try {

? ? ? ? userService.updateUser(user);

? ? } catch (ParamsException e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(e.getCode());

? ? ? ? resultInfo.setMsg(e.getMsg());

? ? }catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("記錄更新失敗!");

? ? }

? ? return resultInfo;

}

@PutMapping("user/{userId}")

@ApiOperation(value = "根據用戶id刪除用戶記錄")

@ApiImplicitParam(name = "userId",value = "查詢參數",required = true,paramType = "path")

public ResultInfo deleteUser(@PathVariable? Integer? userId){

? ? ResultInfo resultInfo=new ResultInfo();

? ? try {

? ? ? ? userService.deleteUser(userId);

? ? } catch (ParamsException e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(e.getCode());

? ? ? ? resultInfo.setMsg(e.getMsg());

? ? }catch (Exception e) {

? ? ? ? e.printStackTrace();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("記錄刪除失敗!");

? ? }

? ? return resultInfo;

}

JavaBean 使用

User.java

@ApiModel(description = "響應結果-用戶信息")

public class User {

? ? @ApiModelProperty(value = "用戶id",example = "0")

? ? private Integer id;

? ? @ApiModelProperty(value = "用戶名")

? ? private String userName;

? ? @ApiModelProperty(value = "用戶密碼")

? ? private String userPwd;

? ? /*

? ? ? 省略get|set

? ? */

}

UserQuery.java

@ApiModel(description = "用戶模塊條件查詢類")

public class UserQuery {

? ? @ApiModelProperty(value = "分頁頁碼",example = "1")

? ? private Integer pageNum=1;

? ? @ApiModelProperty(value = "每頁大小",example = "10")

? ? private Integer pageSize=10;

? ? @ApiModelProperty(value = "用戶名")

? ? private String userName;

? ? /*

? ? ? 省略get|set

? ? */

}

ResultInfo.java

@ApiModel(description = "響應結果-Model信息")

public class ResultInfo {

? ? @ApiModelProperty(value = "響應狀態碼",example = "200")

? ? private Integer code=200;

? ? @ApiModelProperty(value = "響應消息結果")

? ? private String msg="success";

? ? @ApiModelProperty(value = "響應具體結果信息")

? ? private Object result;

? ? /*

? ? ? 省略get|set

? ? */

}

Swagger2 接口文檔訪問

啟動工程,瀏覽器訪問:http://localhost:9999/swagger-ui.html

SpringBoot應用熱部署

什么是熱部署?

熱部署,就是在應用正在運行的時候升級軟件(增加業務/修改bug),卻不需要重新啟動應用

大家都知道在項目開發過程中,常常會改動頁面數據或者修改數據結構,為了顯示改動效果,往往需要重啟應用查看改變效果,其實就是重新編譯生成了新的 Class 文件,這個文件里記錄著和代碼等對應的各種信息,然后 Class 文件將被虛擬機的 ClassLoader 加載。

而熱部署正是利用了這個特點,它監聽到如果有 Class 文件改動了,就會創建一個新的 ClaassLoader 進行加載該文件,經過一系列的過程,最終將結果呈現在我們眼前,Spring Boot通過配置DevTools? 工具來達到熱部署效果。

在原理上是使用了兩個ClassLoader,一個Classloader加載那些不會改變的類(第三方Jar包),另一個ClassLoader加載會更改的類,稱為restart ClassLoader,這樣在有代碼更改的時候,原來的restart ClassLoader 被丟棄,重新創建一個restart ClassLoader,由于需要加載的類相比較少,所以實現了較快的重啟時間。

熱部署環境配置與測試

配置 DevTools 環境

修改 Pom 文件,添加 DevTools 依賴

<!-- DevTools 的坐標 -->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-devtools</artifactId>

? ? <!--當前這個項目被繼承之后,這個不向下傳遞-->

<optional>true</optional>

</dependency>

同時在plugin中添加devtools生效標志

<plugin>

? <groupId>org.springframework.boot</groupId>

? <artifactId>spring-boot-maven-plugin</artifactId>

? <configuration>

? ? ? <fork>true</fork><!-- 如果沒有該配置,熱部署的devtools不生效 -->

? </configuration>

</plugin>

devtools可以實現頁面熱部署(即頁面修改后會立即生效,這個可以直接在application.properties文件中配置spring.thymeleaf.cache=false來實現),實現類文件熱部署(類文件修改后不會立即生效),實現對屬性文件的熱部署。即devtools會監聽classpath下的文件變動,并且會立即重啟應用(發生在保存時機),注意:因為其采用的虛擬機機制,該項重啟是很快的。?配置了后在修改java文件后也就支持了熱啟動,不過這種方式是屬于項目重啟(速度比較快的項目重啟),會清空session中的值,也就是如果有用戶登陸的話,項目重啟后需要重新登陸。

默認情況下,/META-INF/maven,/META-INF/resources,/resources,/static,/templates,/public這些文件夾下的文件修改不會使應用重啟,但是會重新加載(devtools內嵌了一個LiveReload server,當資源發生改變時,瀏覽器刷新)

全局配置文件配置

在application.yml中配置spring.devtools.restart.enabled=false,此時restart類加載器還會初始化,但不會監視文件更新。

spring:

? ## 熱部署配置

? devtools:

? ? restart:

? ? ? enabled: true

? ? ? # 設置重啟的目錄,添加目錄的文件需要restart

? ? ? additional-paths: src/main/java

? ? ? # 解決項目自動重新編譯后接口報404的問題

? ? ? poll-interval: 3000

? ? ? quiet-period: 1000

Idea 配置

當我們修改了Java類后,IDEA默認是不自動編譯的,而spring-boot-devtools又是監測classpath下的文件發生變化才會重啟應用,所以需要設置IDEA的自動編譯

自動編譯配置

File-Settings-Compiler-Build Project automatically

Registry 屬性修改

ctrl + shift + alt + /,選擇Registry,勾上 Compiler autoMake allow when app running

熱部署效果測試

第一次訪問 user/uname/{uname} 接口

@GetMapping("user/uname/{userName}")

@ApiOperation(value = "根據用戶名查詢用戶記錄")

@ApiImplicitParam(name = "userName",value = "查詢參數",required = true,paramType = "path")

public User queryUserByUserName(@PathVariable String userName){

? ? return userService.queryUserByUserName(userName);

}

修改接口代碼 控制臺打印接收的uname參數 ctrl+f9 鍵重新編譯 瀏覽器訪問

@GetMapping("user/uname/{userName}")

@ApiOperation(value = "根據用戶名查詢用戶記錄")

@ApiImplicitParam(name = "userName",value = "查詢參數",required = true,paramType = "path")

public User queryUserByUserName(@PathVariable String userName){

? ? System.out.println("查詢參數-->userName:"+userName);

? ? return userService.queryUserByUserName(userName);

}

SpringBoot單元測試

做過web項目開發的對于單元測試都并不陌生了,通過它能夠快速檢測業務代碼功能的正確與否,SpringBoot框架對單元測試也提供了良好的支持,來看SpringBoot應用中單元測試的使用。

pom.xml 測試依賴添加

<dependency>

? ? <groupId>org.springframework.boot</groupId>

? ? <artifactId>spring-boot-starter-test</artifactId>

</dependency>

Service業務方法測試

這里以UserService為例,src/tets/java 目錄下添加測試包 com.xxxx.sprinboot.service 定義測試類代碼如下:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = {Starter.class})

public class TestUserService {

? ? private Logger log = LoggerFactory.getLogger(TestUserService.class);

? ? @Resource

? ? private UserService userService;

? ? @Before

? ? public void before(){

? ? ? ? log.info("單元測試開始...");

? ? }

? ? @Test

? ? public? void test01(){

? ? ? ? log.info(userService.queryUserByUserId(10).toString());

? ? }

? ? @Test

? ? public? void test02(){

? ? ? ? log.info(userService.queryUserByParams(new UserQuery()).toString());

? ? }

? ? @After

? ? public void after(){

? ? ? ? log.info("單元測試結束...");

? ? }

}

控制層接口方法測試

視圖層代碼使用MockMvc 進行測試,這里以UserCntroller 為例,src/tets/java 目錄下添加測試包 com.xxxx.sprinboot.controller 定義測試類代碼如下:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = {Starter.class})

@AutoConfigureMockMvc

public class TestUserController {

? ? private Logger log = LoggerFactory.getLogger(TestUserController.class);

? ? @Autowired

? ? private MockMvc mockMvc;

? ? //用戶列表查詢

? ? @Test

? ? public void apiTest01()throws Exception{

? ? ? ? MvcResult mvcResult=mockMvc.perform(MockMvcRequestBuilders.get("/user/list")).

? ? ? ? ? ? ? ? andExpect(MockMvcResultMatchers.status().isOk()).andReturn();

? ? ? ? log.info("響應狀態:{}",mvcResult.getResponse().getStatus());

? ? ? ? log.info("響應內容:{}",mvcResult.getResponse().getContentAsString());;

? ? }

? ? // 用戶名記錄查詢

? ? @Test

? ? public void apiTest02()throws Exception{

? ? ? ? MvcResult mvcResult=mockMvc.perform(MockMvcRequestBuilders.get("/user/uname/admin")).

? ? ? ? ? ? ? ? andExpect(MockMvcResultMatchers.status().isOk()).andReturn();

? ? ? ? log.info("響應狀態:{}",mvcResult.getResponse().getStatus());

? ? ? ? log.info("響應內容:{}",mvcResult.getResponse().getContentAsString());;

? ? }

}

分布式緩存Ehcache整合

EhCache是一個比較成熟的Java緩存框架,最早從hibernate發展而來, 是進程中的緩存系統,它提供了用內存,磁盤文件存儲,以及分布式存儲方式等多種靈活的cache管理方案,快速簡單。

Spring Boot對Ehcache的使用提供支持,所以在Spring Boot中只需簡單配置即可使用Ehcache實現數據緩存處理。

Spring Cache 相關注解說明

SpringBoot 內部使用SpringCache 來實現緩存控制,這里集成Ehcache實際上是對SpringCache 抽象的其中一種實現,這里在使用Ehcache實現緩存控制時相關注解說明如下

@CacheConfig

用于標注在類上,可以存放該類中所有緩存的公有屬性,比如設置緩存的名字。

@CacheConfig(cacheNames = "users")

public class UserService {。。。}

這里也可以不使用該注解,直接使用@Cacheable配置緩存集的名字。

@Cacheable

應用到讀取數據的方法上,即可緩存的方法,如查找方法,先從緩存中讀取,如果沒有再調用相應方法獲取數據,然后把數據添加到緩存中。

該注解主要有下面幾個參數:

value、cacheNames:兩個等同的參數(cacheNames為Spring 4新增,作為value的別名),用于指定緩存存儲的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必須有的value屬性,也成為非必需項了

key:緩存對象存儲在Map集合中的key值,非必需,缺省按照函數的所有參數組合作為key值,若自己配置需使用SpEL表達式,比如:@Cacheable(key = "#p0"):使用函數第一個參數作為緩存的key值,更多關于SpEL表達式的詳細內容可參考官方文檔

condition:緩存對象的條件,非必需,也需使用SpEL表達式,只有滿足表達式條件的內容才會被緩存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有當第一個參數的長度小于3的時候才會被緩存。

unless:另外一個緩存條件參數,非必需,需使用SpEL表達式。它不同于condition參數的地方在于它的判斷時機,該條件是在函數被調用之后才做判斷的,所以它可以通過對result進行判斷。

keyGenerator:用于指定key生成器,非必需。若需要指定一個自定義的key生成器,我們需要去實現org.springframework.cache.interceptor.KeyGenerator接口,并使用該參數來指定。需要注意的是:該參數與key是互斥的

cacheManager:用于指定使用哪個緩存管理器,非必需。只有當有多個時才需要使用

cacheResolver:用于指定使用那個緩存解析器,非必需。需通過org.springframework.cache.interceptor.CacheResolver接口來實現自己的緩存解析器,并用該參數指定。

@Cacheable(value = "user", key = "#id")

User selectUserById(final Integer id);

@CachePut

應用到寫數據的方法上,如新增/修改方法,調用方法時會自動把相應的數據放入緩存,@CachePut的參數與@Cacheable類似,示例如下:

@CachePut(value = "user", key = "#user.id")?

public User save(User user) {?

? ? users.add(user);?

? ? return user;?

}?

@CacheEvict

應用到移除數據的方法上,如刪除方法,調用方法時會從緩存中移除相應的數據,示例如下:

@CacheEvict(value = "user", key = "#id")

void delete(final Integer id);

除了同@Cacheable一樣的參數之外,@CacheEvict還有下面兩個參數:

allEntries:非必需,默認為false。當為true時,會移除所有數據

beforeInvocation:非必需,默認為false,會在調用方法之后移除數據。當為true時,會在調用方法之前移除數據。

@Caching

組合多個Cache注解使用。示例:

@Caching(

? ? put = {

? ? ? ? @CachePut(value = "user", key = "#user.id"),

? ? ? ? @CachePut(value = "user", key = "#user.username"),

? ? ? ? @CachePut(value = "user", key = "#user.age")

? }

}

將id-->user;username--->user;age--->user進行緩存。

用戶管理模塊緩存引入

環境配置

pom.xml 依賴添加

<dependency>

? ? <groupId>org.springframework.boot</groupId>

? ? <artifactId>spring-boot-starter-cache</artifactId>

</dependency>

<!-- Ehcache 坐標 -->

<dependency>

? ? <groupId>net.sf.ehcache</groupId>

? ? <artifactId>ehcache</artifactId>

</dependency>

ehcahe.xml 文件添加

src/main/resources 目錄下添加ehcache.xml 文件,內容如下:

<ehcache name="mycache">

? ? <diskStore path="C:\java\cache"/>

? ? <!--

? ? ? ? name:緩存名稱。

? ? ? ? maxElementsInMemory:緩存最大數目

? ? ? ? maxElementsOnDisk:硬盤最大緩存個數。

? ? ? ? eternal:對象是否永久有效,一但設置了,timeout將不起作用。

? ? ? ? overflowToDisk:是否保存到磁盤,當系統宕機時

? ? ? ? timeToIdleSeconds:設置對象在失效前的允許閑置時間(單位:秒)。

? ? ? ? ? ? ? 僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閑置時間無窮大。

? ? ? ? timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。

? ? ? ? ? ? 最大時間介于創建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。

? ? ? ? diskPersistent:是否緩存虛擬機重啟期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.

? ? ? ? diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該有自己的一個緩沖區。

? ? ? ? diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。

? ? ? ? memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。

? ? ? ? ? ? 默認策略是LRU(最近最少使用)。你可以設置為FIFO(先進先出)或是LFU(較少使用)。

? ? ? ? clearOnFlush:內存數量最大時是否清除。

? ? ? ? memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,默認策略)、FIFO(先進先出)、LFU(最少訪問次數)。

? ? ? ? ? ? FIFO,first in first out,這個是大家最熟的,先進先出。

? ? ? ? ? ? LFU, Less Frequently Used,最近最少被訪問的。

? ? ? ? ? ? LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,

? ? ? ? ? ? ? 當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。

? ? ? ? -->

? ? <defaultCache

? ? ? ? ? ? maxElementsInMemory="10000"

? ? ? ? ? ? eternal="false"

? ? ? ? ? ? timeToIdleSeconds="120"

? ? ? ? ? ? timeToLiveSeconds="120"

? ? ? ? ? ? maxElementsOnDisk="10000000"

? ? ? ? ? ? diskExpiryThreadIntervalSeconds="120"

? ? ? ? ? ? memoryStoreEvictionPolicy="LRU">

? ? </defaultCache>

? ? <cache

? ? ? ? ? ? name="users"

? ? ? ? ? ? eternal="false"

? ? ? ? ? ? maxElementsInMemory="100"

? ? ? ? ? ? overflowToDisk="false"

? ? ? ? ? ? diskPersistent="false"

? ? ? ? ? ? timeToIdleSeconds="0"

? ? ? ? ? ? timeToLiveSeconds="300"

? ? ? ? ? ? memoryStoreEvictionPolicy="LRU"/>

</ehcache>

application.yml 添加緩存配置

spring:

? datasource:

? ? type: com.mchange.v2.c3p0.ComboPooledDataSource

? ? driver-class-name: com.mysql.cj.jdbc.Driver

? ? url: jdbc:mysql://127.0.0.1:3306/springboot_mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8

? ? username: root

? ? password: root

? devtools:

? ? restart:

? ? ? enabled: true

? ? ? # 設置重啟的目錄,添加目錄的文件需要restart

? ? ? additional-paths: src/main/java

? ? ? # 解決項目自動重新編譯后接口報404的問題

? ? ? poll-interval: 3000

? ? ? quiet-period: 1000

? cache:

? ? ehcache:

? ? ? config: classpath:ehcahe.xml

Starter 啟動入口類啟動緩存

@MapperScan("com.xxxx.springboot.dao")

@EnableCaching

@SpringBootApplication

public class Starter {

? ? public static void main(String[] args) {

? ? ? ? SpringApplication.run(Starter.class);

? ? }

}

緩存User 對象實現序列化接口

@ApiModel(description = "用戶實體對象")

public class User implements Serializable {

? ? @ApiModelProperty(value = "用戶id主鍵")

? ? private Integer id;

? ? @ApiModelProperty(value = "用戶名")

? ? private String userName;

? ? @ApiModelProperty(value = "用戶密碼")

? ? private String userPwd;

? ? /*

? ? ? 省略 get|set方法

? ? */

}

緩存代碼添加

這里以UserService 方法為例

用戶詳情查詢緩存添加

@Cacheable(value = "users",key = "#userId")

public User queryUserByUserId(Integer userId){

? ? return userMapper.queryById(userId);

}

用戶列表查詢緩存

@Cacheable(value = "users",key="#userQuery.userName+'-'+#userQuery.pageNum+'-'+#userQuery.pageSize")

public PageInfo<User> queryUserByParams(UserQuery userQuery){

? ? PageHelper.startPage(userQuery.getPageNum(),userQuery.getPageSize());

? ? return new PageInfo<User>(userMapper.selectByParams(userQuery));

}

用戶更新&刪除緩存清除

@Transactional(propagation = Propagation.REQUIRED)

@CacheEvict(value = "users",key="#user.id")

public void updateUser(User user) {

? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用戶名不能為空!");

? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用戶密碼不能為空!");

? ? User temp = userMapper.queryUserByUserName(user.getUserName());

? ? AssertUtil.isTrue(null != temp && !(temp.getId().equals(user.getId())), "該用戶已存在!");

? ? AssertUtil.isTrue(userMapper.update(user)<1,"用戶記錄添加失敗!");

}

@Transactional(propagation = Propagation.REQUIRED)

@CacheEvict(value = "users",allEntries=true)

public? void deleteUser(Integer userId){

? ? AssertUtil.isTrue(null == userId || null ==userMapper.queryById(userId),"待刪除記錄不存在!");

? ? AssertUtil.isTrue(userMapper.delete(userId)<1,"用戶刪除失敗!");

}

定時調度集成-Quartz

在日常項目運行中,我們總會有需求在某一時間段周期性的執行某個動作。比如每天在某個時間段導出報表,或者每隔多久統計一次現在在線的用戶量等。

在Spring Boot中有Java自帶的java.util.Timer類,SpringBoot自帶的Scheduled來實現,也有強大的調度器Quartz。Scheduled 在Spring3.X 引入,默認SpringBoot自帶該功能,使用起來也很簡單,在啟動類級別添加@EnableScheduling注解即可引入定時任務環境。但遺憾的是Scheduled? 默認不支持分布式環境,這里主要講解Quartz 時鐘調度框架與Spring Boot 集成。

環境整合配置

<dependency>

? ? <groupId>org.springframework.boot</groupId>

? ? <artifactId>spring-boot-starter-quartz</artifactId>

</dependency>

源代碼添加

定義job

com.xxxx.springboot下添加jobs包,定義待執行job任務

public class MyFirstJob implements Job {


? ? private Logger log = LoggerFactory.getLogger(MyFirstJob.class);

? ? @Override

? ? public void execute(JobExecutionContext context) throws JobExecutionException {

? ? ? ? SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

? ? ? ? TriggerKey triggerKey =? context.getTrigger().getKey();

? ? ? ? log.info("觸發器:"+triggerKey.getName()+"-->所屬組:"+triggerKey.getGroup()+"-->"+sdf.format(new Date())+"-->"+"hello Spring Boot Quartz...");

? ? }

}

構建調度配置類

@Configuration

public class QuartzConfig {

? ? @Bean

? ? public JobDetail jobDetail1(){

? ? ? ? return JobBuilder.newJob(MyFirstJob.class).storeDurably().build();

? ? }

? ? @Bean

? ? public Trigger trigger1(){

? ? ? ? SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()

? ? ? ? ? ? ? ? //每一秒執行一次

? ? ? ? ? ? ? ? .withIntervalInSeconds(1)

? ? ? ? ? ? ? ? //永久重復,一直執行下去

? ? ? ? ? ? ? ? .repeatForever();

? ? ? ? return TriggerBuilder.newTrigger()

? ? ? ? ? ? ? ? .withIdentity("trigger1","group1")

? ? ? ? ? ? ? ? .withSchedule(scheduleBuilder)

? ? ? ? ? ? ? ? .forJob(jobDetail1())

? ? ? ? ? ? ? ? .build();

? ? }

? ? // 每兩秒觸發一次任務

? ? @Bean

? ? public Trigger trigger2(){

? ? ? ? return TriggerBuilder.newTrigger()

? ? ? ? ? ? ? ? .withIdentity("trigger2", "group1")

? ? ? ? ? ? ? ? .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *"))

? ? ? ? ? ? ? ? .forJob(jobDetail1())

? ? ? ? ? ? ? ? .build();

? ? }

}

啟動StarterApplication 查看控制臺打印效果

全局異常與事物控制

Spring Boot事物支持

在使用Jdbc 作為數據庫訪問技術時,Spring Boot框架定義了基于jdbc 的PlatformTransactionManager 接口的實現DataSourceTransactionManager,并在Spring Boot 應用啟動時自動進行配置。如果使用jpa 的話 Spring Boot 同樣提供了對應實現。

這里Spring Boot 集成了Mybatis框架,Mybatis底層數據訪問層實現基于jdbc 來實現,所以在Spring Boot 環境下對事物進行控制,事物實現由Spring Boot實現并自動配置,在使用時通過注解方式標注相關方法加入事物控制即可

聲明式事物配置

@Transactional(propagation = Propagation.REQUIRED)

? ? public void saveUser(User user) {

? ? ? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用戶名不能為空!");

? ? ? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用戶密碼不能為空!");

? ? ? ? User temp = userMapper.queryUserByUserName(user.getUserName());

? ? ? ? AssertUtil.isTrue(null != temp, "該用戶已存在!");

? ? ? ? AssertUtil.isTrue(userMapper.save(user)<1,"用戶記錄添加失敗!");

? ? }

? ? @Transactional(propagation = Propagation.REQUIRED)

? ? public void updateUser(User user) {

? ? ? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()), "用戶名不能為空!");

? ? ? ? AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"用戶密碼不能為空!");

? ? ? ? User temp = userMapper.queryUserByUserName(user.getUserName());

? ? ? ? AssertUtil.isTrue(null != temp && !(temp.getId().equals(user.getId())), "該用戶已存在!");

? ? ? ? AssertUtil.isTrue(userMapper.update(user)<1,"用戶記錄添加失敗!");

? ? }

? ? @Transactional(propagation = Propagation.REQUIRED)

? ? public? void deleteUser(Integer id){

? ? ? ? AssertUtil.isTrue(null == id || null ==userMapper.queryById(id),"待刪除記錄不存在!");

? ? ? ? AssertUtil.isTrue(userMapper.delete(id)<1,"用戶刪除失敗!");

? ? }

Spring Boot 全局異常處理

SpringMvc 中對異常統一處理提供了相應處理方式,推薦大家使用的是實現接口HandlerExceptionResolver的方式,對代碼侵入性較小。

在Spring Boot 應用中同樣提供了對異常的全局性處理,相關注解如下:

@ControllerAdvice

該注解組合了@Component注解功能,最常用的就是作為全局異常處理的切面類,同時通過該注解可以指定包掃描的范圍。@ControllerAdvice約定了幾種可行的返回值,如果是直接返回model類的話,需要使用@ResponseBody進行json轉換

@ExceptionHandler

? 該注解在Spring 3.X 版本引入,在處理異常時標注在方法級別,代表當前方法處理的異常類型有哪些 具體應用以Restful 接口為例,測試保存用戶接口

全局異常應用

異常拋出與全局捕捉

UserController 查詢接口

@ApiOperation(value = "根據用戶id查詢用戶記錄")

@ApiImplicitParam(name = "userId",value = "查詢參數",required = true,paramType = "path")

@GetMapping("user/{userId}")

public User queryUserByUserId(@PathVariable? Integer userId){

? ? return userService.queryUserByUserId(userId);

}

UserService 查詢業務方法,拋出ParamExceptions 異常

public User queryUserByUserId(Integer userId){

? ? AssertUtil.isTrue(true,"異常測試...");

? ? return userMapper.queryById(userId);

}

全局異常處理類GlobalExceptionHandler定義

@ControllerAdvice

public class GlobalExceptionHandler{

? ? /**

? ? * 全局異常處理 返回json

? ? * @param e

? ? * @return

? ? */

? ? @ExceptionHandler(value = Exception.class)

? ? @ResponseBody

? ? public ResultInfo exceptionHandler(Exception e){

? ? ? ? ResultInfo resultInfo=new ResultInfo();

? ? ? ? resultInfo.setCode(300);

? ? ? ? resultInfo.setMsg("操作失敗!");

? ? ? ? if(e instanceof ParamsException){

? ? ? ? ? ? ParamsException pe= (ParamsException) e;

? ? ? ? ? ? resultInfo.setMsg(pe.getMsg());

? ? ? ? ? ? resultInfo.setCode(pe.getCode());

? ? ? ? }

? ? ? ? return resultInfo;

? ? }

}

Postman 執行測試效果

特定異常處理

通過@ExceptionHandler 標注方法可以處理特定異常,這里以用戶未登錄異常為例,通過全局異常進行統一處理

/**

* 用戶未登錄異常特殊處理 返回json

* @param authExceptions

* @return

*/

@ExceptionHandler(value = NoLoginException.class)

@ResponseBody

public? ResultInfo userNotLoginHandler(NoLoginException authExceptions){

? ? System.out.println("用戶未登錄異常處理。。。");

? ? return new ResultInfo(authExceptions.getCode(),authExceptions.getMsg());

}

在用戶添加接口中拋出未登錄異常為例進行測試

@PutMapping("user")

@ApiOperation(value = "用戶添加")

@ApiImplicitParam(name = "user",value = "用戶實體類",dataType = "User")

public ResultInfo saveUser(@RequestBody? User user){

? ? if(1==1){

? ? ? ? throw? new NoLoginException();

? ? }

? ? ResultInfo resultInfo=new ResultInfo();

? ? ? ? userService.saveUser(user);

? ? return resultInfo;

}

SpringBoot 數據校驗-Validation

日常項目開發中,對于前端提交的表單,后臺接口接收到表單數據后,為了程序的嚴謹性,通常后端會加入業務參數的合法校驗操作來避免程序的非技術性bug,這里對于客戶端提交的數據校驗,SpringBoot通過spring-boot-starter-validation 模塊包含了數據校驗的工作。

這里主要介紹Spring Boot中對請求數據進行校驗,相關概念如下

JSR303/JSR-349: JSR303是一項標準,只提供規范不提供實現,規定一些校驗規范即校驗注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其升級版本,添加了一些新特性。

Hibernate Validation:Hibernate Validation是對這個規范的實現,并增加了一些其他校驗注解,如@Email,@Length,@Range等等

Spring Validation:Spring Validation對Hibernate Validation進行了二次封裝,在Spring Mvc模塊中添加了自動校驗,并將校驗信息封裝進了特定的類中

環境配置

實現參數校驗,程序必須引入spring-boot-starter-validation 依賴,只是在引入spring-boot-starter-web依賴時,該模塊會自動依賴spring-boot-starter-validation,所以程序中引入spring-boot-starter-web 會一并依賴spring-boot-starter-validation到項目中。

校驗相關注解

注解功能

@AssertFalse可以為null,如果不為null的話必須為false

@AssertTrue可以為null,如果不為null的話必須為true

@DecimalMax設置不能超過最大值

@DecimalMin設置不能超過最小值

@Digits設置必須是數字且數字整數的位數和小數的位數必須在指定范圍內

@Future日期必須在當前日期的未來

@Past日期必須在當前日期的過去

@Max最大不得超過此最大值

@Min最大不得小于此最小值

@NotNull不能為null,可以是空

@Pattern必須滿足指定的正則表達式

@Size集合、數組、map等的size()值必須在指定范圍內

@Email必須是email格式

@Length長度必須在指定范圍內

@NotBlank字符串不能為null,字符串trin()后也不能等于“”

@NotEmpty不能為null,集合、數組、map等size()不能為0;字符串trin()后可以等于“”

@Range值必須在指定范圍內

@URL必須是一個URL

參數校驗注解使用

User實體類參數校驗注解

public class User? implements Serializable {

? ? private Integer id;

? ? @NotBlank(message = "用戶名不能為空!")

? ? private String userName;

? ? @NotBlank(message = "用戶密碼不能為空!")

? ? @Length(min = 6, max = 10,message = "密碼長度至少6位但不超過10位!")

? ? private String userPwd;

? ? @Email

? ? private String email;


? ? /*

? ? ? 省略get set 方法?

? ? */

}

接口方法形參@Valid注解添加

@PostMapping("user02")

@ApiOperation(value = "用戶添加")

@ApiImplicitParam(name = "user02",value = "用戶實體類",dataType = "User")

public ResultInfo saveUser02(@Valid? User user){

? ? ResultInfo resultInfo=new ResultInfo();

? ? //userService.saveUser(user);

? ? return resultInfo;

}

全局異常錯誤信息捕捉

/**

* 全局異常處理 返回json

* @param e

* @return

*/

@ExceptionHandler(value = Exception.class)

@ResponseBody

public ResultInfo exceptionHandler(Exception e){

? ? ResultInfo resultInfo=new ResultInfo();

? ? resultInfo.setCode(300);

? ? resultInfo.setMsg("操作失敗!");

? ? if(e instanceof ParamsException){

? ? ? ? ParamsException pe= (ParamsException) e;

? ? ? ? resultInfo.setMsg(pe.getMsg());

? ? ? ? resultInfo.setCode(pe.getCode());

? ? }else if(e instanceof BindException){

? ? ? ? BindException be = (BindException) e;

? ? ? ? resultInfo.setResult(be.getBindingResult().getFieldError().getDefaultMessage());

? ? }

? ? return resultInfo;

}

PostMan 接口測試

總結

今天課程主要介紹了SpringBoot中各種環境的整合與測試工作,持久層框架Mybatis集成與數據訪問基本操作,借助SpringBoot單元測試實現業務方法與控制器接口測試,同時集成了Swagger2 接口文件生成工具來快速生成接口文檔的功能。在web項目開發中常見的項目熱部署配置,這里集成DevTools工具來幫助web開發者在項目開發中不斷手動啟動服務器部署項目的繁瑣流程,通過引入EhCache 緩存技術來加快應用程序數據的訪問效率,然后對于項目中常見的定時任務的執行,這里集成了Quartz 任務調度框架來實現任務定時執行處理,最后提到了SpringBoot 應用中對象項目事物控制與異常統一處理,從而提高項目代碼的健壯性與數據的一致性,借助SpringBoot 的Validation 實現后端數據參數的校驗機制,結合全局異常來對校驗結果進行輸出操作,提高后端應參數處理的嚴謹性。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380