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 實現后端數據參數的校驗機制,結合全局異常來對校驗結果進行輸出操作,提高后端應參數處理的嚴謹性。



喜歡的朋友記得點贊、收藏、關注哦!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/64717.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/64717.shtml
英文地址,請注明出處:http://en.pswp.cn/web/64717.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SparseViT:基于稀疏編碼Transformer的非語義中心、參數高效的圖像篡改定位

摘要 https://arxiv.org/pdf/2412.14598 非語義特征或語義無關特征&#xff0c;與圖像上下文無關但對圖像篡改敏感&#xff0c;被認為是圖像篡改定位&#xff08;IML&#xff09;的重要證據。由于無法獲得人工標簽&#xff0c;現有工作依賴于手工方法提取非語義特征。手工非語…

Redisson 分布式鎖獲取tryLock和lock的區別

問題 boolean isLock lock.tryLock(10, 30, TimeUnit.SECONDS); boolean isLock lock.lock(30, TimeUnit.SECONDS); boolean isLock lock.lock(); 三者的區別&#xff1f;&#xff1f; 這三個方法都是用于獲取 Redisson 分布式鎖的&#xff0c;但它們在獲取鎖的方式和行為…

【git】git生成rsa公鑰的方法

git生成rsa公鑰的方法 一&#xff0c;簡介二&#xff0c;操作方法三&#xff0c;總結 一&#xff0c;簡介 在工作的過程中&#xff0c;經常需要生成rsa的密鑰&#xff0c;然后提供給別人&#xff0c;然后別人給你開通代碼下載權限。本文介紹如何在本地生成rsa的密鑰供參考。 …

Zookeeper模式安裝Kafka(含常規、容器兩種安裝方式)

一、#創作靈感# 公司使用Kafka的軟件項目較多&#xff0c;故寫技術筆記鞏固知識要點 二、軟件環境 - Kafka 3.9.0 官方下載地址&#xff1a;Kafka 3.9.0 - ZooKeeper 3.9.3 官方下載地址&#xff1a;ZooKeeper 3.9.3 - Docker Desktop 4.37 容器圖形化工具 官方下載地址…

7.傅里葉級數練習題

7.傅里葉級數練習題 設函數&#xff1a; f ( x ) { ? x , 0 ≤ x ≤ 1 2 , 2 ? 2 x , 1 2 < x < 1 , f(x) \begin{cases} -x, & 0 \leq x \leq \frac{1}{2}, \\ 2 - 2x, & \frac{1}{2} < x < 1, \end{cases} f(x){?x,2?2x,?0≤x≤21?,21?<x&…

【高項】信息系統項目管理師(二)項目管理概論

一、PMBOK的發展 項目管理知識體系&#xff08;PMBOK&#xff09;是由美國項目管理協會&#xff08;PMI&#xff09;開發的一套描述項目管理專業范圍的知識體系&#xff0c;包含了對項目管理所需的知識、技能和工具的描述。 二、項目基本要素 2.1 項目基礎 項目是為提供一項…

C++設計模式:狀態模式(自動售貨機)

什么是狀態模式&#xff1f; 狀態模式是一種行為型設計模式&#xff0c;它允許一個對象在其內部狀態發生改變時&#xff0c;動態改變其行為。通過將狀態相關的邏輯封裝到獨立的類中&#xff0c;狀態模式能夠將狀態管理與行為解耦&#xff0c;從而讓系統更加靈活和可維護。 通…

【Pytorch實用教程】循環神經網絡中使用dropout需要注意的問題

文章目錄 問題解答警告的具體含義解決方案示例代碼總結問題 UserWarning: dropout option adds dropout after all but last recurrent layer, so non-zero dropout expects num_layers greater than 1, but got dropout=0.3 and num_layers=1 warnings.warn("dropout op…

數據中臺與數據治理服務方案[50頁PPT]

本文概述了數據中臺與數據治理服務方案的核心要點。數據中臺作為政務服務數據化的核心&#xff0c;通過整合各部門業務系統數據&#xff0c;進行建模與加工&#xff0c;以新數據驅動政府管理效率提升與政務服務能力增強。數據治理則聚焦于解決整體架構問題&#xff0c;確保數據…

postgresq-自定義執行計劃(custom plan)與generic plan(通用執行計劃)

文章目錄 之前寫過一篇關于 PostgreSQL prepare sql的文章&#xff0c;但當時沒有提到generic plan(通用計劃)和custom plan(自定義計劃)這兩個概念。現在將通過舉例介紹這兩個概念。 創建測試表&#xff1a; postgres# create database demo; CREATE DATABASE postgres# \c d…

dockfile 配置 /etc/apt/source.list.d/debian.list 清華鏡像

docker:3.12.7 鏡像使用的是 debian 系統&#xff0c;比 ubuntu 更輕量。debian 系統內&#xff0c;apt 鏡像源列表位于 /etc/apt/source.list.d/debian.list&#xff08;作為對比&#xff0c;ubuntu 的鏡像列表位于 /etc/apt/source.list&#xff0c;二者語法相同&#xff09;…

程序員測試日常小工具

作為一名程序員&#xff0c;或者測試人員&#xff0c;日常工作最常用的工具有哪些&#xff0c;截圖&#xff0c;截圖漂浮&#xff0c;翻譯&#xff0c;日期處理&#xff0c;api調用...&#xff0c; 當你拿到一串報文后&#xff0c;想要json轉換時&#xff0c;是不是要打…

【MySQL高級】第1-4章

第1章 存儲過程 1.1 什么是存儲過程&#xff1f; 存儲過程可稱為過程化SQL語言&#xff0c;是在普通SQL語句的基礎上增加了編程語言的特點&#xff0c;把數據操作語句(DML)和查詢語句(DQL)組織在過程化代碼中&#xff0c;通過邏輯判斷、循環等操作實現復雜計算的程序語言。 換…

深入淺出:AWT事件監聽器及其應用

前言 在Java的GUI編程中&#xff0c;事件處理是非常重要的一環。AWT&#xff08;Abstract Window Toolkit&#xff09;框架提供了靈活的事件處理機制&#xff0c;使得開發者能夠響應用戶的操作&#xff0c;例如點擊按鈕、鍵盤輸入、鼠標點擊等。AWT的事件監聽器就是實現這一機…

【Rust自學】8.5. HashMap Pt.1:HashMap的定義、創建、合并與訪問

8.5.0. 本章內容 第八章主要講的是Rust中常見的集合。Rust中提供了很多集合類型的數據結構&#xff0c;這些集合可以包含很多值。但是第八章所講的集合與數組和元組有所不同。 第八章中的集合是存儲在堆內存上而非棧內存上的&#xff0c;這也意味著這些集合的數據大小無需在編…

混合合并兩個pdf文件

混合兩個pdf 1、在線免費交替和混合奇數和偶數PDF頁面2、有什么軟件把兩個 PDF 交叉合并&#xff1f;3、pdfsam本地合并 如何Google翻譯的原文和譯文合并&#xff0c;&#xff08;沉浸式翻譯效果相對較好&#xff09; 1、在線免費交替和混合奇數和偶數PDF頁面 https://deftpd…

Hutool 發送 HTTP 請求的幾種常見寫法

最簡單的 GET 請求&#xff1a; String result HttpUtil.get("https://www.baidu.com");帶參數的 GET 請求&#xff1a; // 方法1: 直接拼接URL參數 String result HttpUtil.get("https://www.baidu.com?name張三&age18");// 方法2: 使用 HashMap…

獲取用戶詳細信息-ThreadLocal優化

Thread全局接口可用&#xff0c;不用再重復編寫。所以為了代碼的復用&#xff0c;使用Thread。把之前的內容&#xff08;函數的參數和map與username&#xff09;注釋掉&#xff0c;換為Thread傳過來的內容&#xff08;map與username&#xff09;。 因為Thread需要在攔截器里面…

THUCNews解壓/THUCNews數據集解壓出問題

省流&#xff1a;使用zip64進行解壓&#xff0c;文件數目太多windows默認zip16裝不下 我在使用THUCNews中文文本數據集時出現了問題&#xff0c;原數據集解壓后應該包含以下兩個文件夾: 其中THUCNews文件夾下有以新聞類別命名的子文件。官網下載的是一個1.56GB的zip壓縮包 而我…

MySQL使用通用二進制文件安裝到Unix/Linux

Oracle提供了一組MySQL的二進制發行版。其中包括用于許多平臺的壓縮tar文件&#xff08;擴展名為.tar.xz的文件&#xff09;形式的通用二進制發行版&#xff0c;以及用于選定平臺的特定平臺包格式的二進制文件。 本節介紹在Unix/Linux平臺上從壓縮的tar文件二進制分布安裝MySQ…