一份 Spring Boot 項目搭建模板 ~

來源:juejin.im/post/6844904083942277127
前言 基礎(chǔ)項目該包含哪些東西。 Swagger CodeGenerator代碼生成器。 常用的封裝 通用的分頁對象 常用工具類 異常處理 多環(huán)境配置 日志配置 JenkinsFile 代碼地址 結(jié)尾
前言
建立一個全新的項目,或者把舊的龐大的項目,進行拆分成多個項目。在建立新的項目中,經(jīng)常需要做一些重復的工作,比如說拷貝一下常用的工具類,通用代碼等等。所以就可以做一個基礎(chǔ)的項目方便使用,在經(jīng)歷新項目的時候,直接在基礎(chǔ)項目上進行簡單配置就可以開發(fā)業(yè)務(wù)代碼了。
基礎(chǔ)項目該包含哪些東西。
Swagger在線接口文檔。 CodeGenerator 代碼生成器。 統(tǒng)一返回。 通用的分頁對象。 常用工具類。 全局異常攔截。 錯誤枚舉。 自定義異常。 多環(huán)境配置文件。 Maven多環(huán)境配置。 日志配置。 JenkinsFile。
可以在評論區(qū)進行補充
Swagger
寫接口文檔通常是一件比較頭疼的事情,然而swagger就用是用來幫我們解決這個問題的。可以在線生成接口文檔,并且可以在頁面上進行測試。

可以非常清楚的顯示,請求數(shù)據(jù)已經(jīng)響應(yīng)數(shù)據(jù)。當然這一切都需要在代碼中進行配置。

注意的點:接口文檔只能在測試/開發(fā)環(huán)境開啟,其他環(huán)境請關(guān)閉。
常用的Swagger注解
@Api用于Controller@ApiOperation用于Controller內(nèi)的方法。@ApiResponses用于標識接口返回數(shù)據(jù)的類型。@ApiModel用于標識類的名稱@ApiModelProperty用于標識屬性的名稱
案例
@RestController
@Api(tags?=?"用戶")
@AllArgsConstructor
@RequestMapping("/user")
public?class?UserController?{
????private?IUserService?userService;
????/**
?????*?獲取用戶列表
?????*?@param?listUserForm?表單數(shù)據(jù)
?????*?@return?用戶列表
?????*/
????@ApiOperation("獲取用戶列表")
????@GetMapping("/listUser")
????@ApiResponses(
????????????@ApiResponse(code?=?200,?message?=?"操作成功",?response?=?UserVo.class)
????)
????public?ResultVo?listUser(@Validated?ListUserForm?listUserForm){
????????return?ResultVoUtil.success(userService.listUser(listUserForm));
????}
}
@Data
@ApiModel("獲取用戶列表需要的表單數(shù)據(jù)")
@EqualsAndHashCode(callSuper?=?false)
public?class?ListUserForm?extends?PageForm<ListUserForm>?{
????/**
?????*?用戶狀態(tài)
?????*/
????@ApiModelProperty("用戶狀態(tài)")
????@NotEmpty(message?=?"用戶狀態(tài)不能為空")
????@Range(min?=??-1?,?max?=?1?,?message?=?"用戶狀態(tài)有誤")
????private?String?status;
}
對應(yīng)的swagger的配置可以查看基礎(chǔ)項目內(nèi)的SwaggerConfiguration.java.
CodeGenerator代碼生成器。
mybatis_plus代碼生成器可以幫我們生成entity,service,serviceImpl,mapper,mapper.xml。省去了建立一大堆實體類的麻煩。
由于配置太長這里就不貼出來了,對應(yīng)的CodeGenerator的配置可以查看基礎(chǔ)項目內(nèi)的CodeGenerator.java.
常用的封裝
統(tǒng)一返回 ResultVo
將所有的接口的響應(yīng)數(shù)據(jù)的格式進行統(tǒng)一。
@Data
@ApiModel("固定返回格式")
public?class?ResultVo?{
????/**
?????*?錯誤碼
?????*/
????@ApiModelProperty("錯誤碼")
????private?Integer?code;
????/**
?????*?提示信息
?????*/
????@ApiModelProperty("提示信息")
????private?String?message;
????/**
?????*?具體的內(nèi)容
?????*/
????@ApiModelProperty("響應(yīng)數(shù)據(jù)")
????private?Object?data;
}
抽象表單 BaseForm
public?abstract?class?BaseForm<T>?{
????/**
?????*?獲取實例
?????*?@return?返回實體類
?????*/
????public?abstract?T?buildEntity();
}
有小伙伴可能有疑問了,這個類有啥用呢。先看一下,下面的代碼。
????/**
?????*?添加用戶
?????*?@param?userForm?表單數(shù)據(jù)
?????*?@return?true?或者?false
?????*/
????@Override
????public?boolean?addUser(AddUserForm?userForm)?{
????????User?user?=?new?User();
????????user.setNickname(userForm.getNickname());
????????user.setBirthday(userForm.getBirthday());
????????user.setUsername(userForm.getUsername());
????????user.setPassword(userForm.getPassword());
????????return?save(user);
????}
重構(gòu)一下,感覺清爽了一些。
/**
?*?添加用戶
?*?@param?userForm?表單數(shù)據(jù)
?*?@return?true?或者?false
?*/
@Override
public?boolean?addUser(AddUserForm?userForm)?{
????User?user?=?new?User();
????BeanUtils.copyProperties(this,user);
????return?save(user);
}
使用BaseForm進行重構(gòu) AddUserForm 繼承 BaseForm并重寫buildEntity
@Data
@EqualsAndHashCode(callSuper?=?false)
public?class?AddUserForm?extends?BaseForm<User>?{
????/**
?????*?昵稱
?????*/
????private?String?nickname;
????/**
?????*?生日
?????*/
????private?Date?birthday;
????/**
?????*?用戶名
?????*/
????private?String?username;
????/**
?????*?密碼
?????*/
????private?String?password;
????/**
?????*?構(gòu)造實體
?????*?@return?實體對象
?????*/
????@Override
????public?User?buildEntity()?{
????????User?user?=?new?User();
????????BeanUtils.copyProperties(this,user);
????????return?user;
????}
}
/**
?*?添加用戶
?*?@param?userForm?表單數(shù)據(jù)
?*?@return?true?或者?false
?*/
@Override
public?boolean?addUser(AddUserForm?userForm)?{
????return?save(userForm.buildEntity());
}
上面的代碼有沒有種似曾相識的感覺,很多情況都是將接受到的參數(shù),轉(zhuǎn)變成對應(yīng)的實體類然后保存或者更新。所以對于這類的form可以繼承baseform并實現(xiàn)buildEntity()這樣可以更加符合面向?qū)ο螅?code style>service不需要關(guān)心form如何轉(zhuǎn)變成entity,只需要在使用的時候調(diào)用buildEntity()即可,尤其是在form -> entity相對復雜的時候,這樣做可以減少service內(nèi)的代碼。讓代碼邏輯看起來更加清晰。
通用的分頁對象
涉及到查詢的時候,絕大多數(shù)都需要用到分頁,所以說封裝分頁對象就很有必要。可以注意下 PageForm.calcCurrent()、PageVo.setCurrentAndSize()、PageVo.setTotal()這個幾個方法。
PageForm
@Data
@ApiModel(value?=?"分頁數(shù)據(jù)",?description?=?"分頁需要的表單數(shù)據(jù)")
public?class?PageForm<T?extends?PageForm>>{
????/**
?????*?頁碼
?????*/
????@ApiModelProperty(value?=?"頁碼?從第一頁開始?1")
????@Min(value?=?1,?message?=?"頁碼輸入有誤")
????private?Integer?current;
????/**
?????*?每頁顯示的數(shù)量
?????*/
????@ApiModelProperty(value?=?"每頁顯示的數(shù)量?范圍在1~100")
????@Range(min?=?1,?max?=?100,?message?=?"每頁顯示的數(shù)量輸入有誤")
????private?Integer?size;
????/**
?????*?計算當前頁?,方便mysql?進行分頁查詢
?????*?@return?返回?pageForm
?????*/
????@ApiModelProperty(hidden?=?true)
????public?T?calcCurrent(){
????????current?=?(current?-?1?)?*?size;
????????return?(T)?this;
????}
}
PageVo
@Data
public?class?PageVo<T>?{
????/**
?????*?分頁數(shù)據(jù)
?????*/
????@ApiModelProperty(value?=?"分頁數(shù)據(jù)")
????private?List?records;
????/**
?????*?總條數(shù)
?????*/
????@ApiModelProperty(value?=?"總條數(shù)")
????private?Integer?total;
????/**
?????*?總頁數(shù)
?????*/
????@ApiModelProperty(value?=?"總頁數(shù)")
????private?Integer?pages;
????/**
?????*?當前頁
?????*/
????@ApiModelProperty(value?=?"當前頁")
????private?Integer?current;
????/**
?????*?查詢數(shù)量
?????*/
????@ApiModelProperty(value?=?"查詢數(shù)量")
????private?Integer?size;
????/**
?????*?設(shè)置當前頁和每頁顯示的數(shù)量
?????*?@param?pageForm?分頁表單
?????*?@return?返回分頁信息
?????*/
????@ApiModelProperty(hidden?=?true)
????public?PageVo?setCurrentAndSize(PageForm>?pageForm) {
????????BeanUtils.copyProperties(pageForm,this);
????????return?this;
????}
????/**
?????*?設(shè)置總記錄數(shù)
?????*?@param?total?總記錄數(shù)
?????*/
????@ApiModelProperty(hidden?=?true)
????public?void?setTotal(Integer?total)?{
????????this.total?=?total;
????????this.setPages(this.total?%?this.size?>?0???this.total?/?this.size?+?1?:?this.total?/?this.size);
????}
}
案例
ListUserForm
@Data
@ApiModel("獲取用戶列表需要的表單數(shù)據(jù)")
@EqualsAndHashCode(callSuper?=?false)
public?class?ListUserForm?extends?PageForm<ListUserForm>?{
????/**
?????*?用戶狀態(tài)
?????*/
????@ApiModelProperty("用戶狀態(tài)")
????@NotEmpty(message?=?"用戶狀態(tài)不能為空")
????@Range(min?=??-1?,?max?=?1?,?message?=?"用戶狀態(tài)有誤")
????private?String?status;
}
UserServiceImpl
/**
?*?獲取用戶列表
?*?@param?listUserForm?表單數(shù)據(jù)
?*?@return?用戶列表
?*/
@Override
public?PageVo?listUser(ListUserForm?listUserForm)? {
????PageVo?pageVo?=?new?PageVo().setCurrentAndSize(listUserForm);
????pageVo.setTotal(countUser(listUserForm.getStatus()));
????pageVo.setRecords(userMapper.listUser(listUserForm.calcCurrent()));
????return?pageVo;
}
/**
?*?獲取用戶數(shù)量
?*?@param?status?狀態(tài)
?*?@return?用戶數(shù)量
?*/
private?Integer?countUser(String?status){
????return?count(new?QueryWrapper().eq("status",status));
}
UserController
/**
?*?獲取用戶列表
?*?@param?listUserForm?表單數(shù)據(jù)
?*?@return?用戶列表
?*/
@ApiOperation("獲取用戶列表")
@GetMapping("/listUser")
@ApiResponses(
????????@ApiResponse(code?=?200,?message?=?"操作成功",?response?=?UserVo.class)
)
public?ResultVo?listUser(@Validated?ListUserForm?listUserForm){
????return?ResultVoUtil.success(userService.listUser(listUserForm));
}

注意的點
PageVo在實例化的時候需要設(shè)置當前頁和每頁顯示的數(shù)量 可以調(diào)用 setCurrentAndSize()完成。進行分頁查詢的時候,需要計算偏移量。 listUserForm.calcCurrent()
為什么要計算偏移量呢?
假如查詢第1頁每頁顯示10條記錄,前端傳遞過來的參數(shù)是 current=1&&size=10,這個時候limit 1,10沒有問題。假如查詢第2頁每頁顯示10條記錄,前端傳遞過來的參數(shù)是 current=2&&size=10,這個時候limit 2,10就有問題,實際應(yīng)該是limit 10,10。calcCurrent()的作用就是如此。
為什么不用MybatisPlus自帶的分頁插件呢?
自帶的分頁查詢在大量數(shù)據(jù)下,會出現(xiàn)性能問題。
常用工具類
常用工具類可以根據(jù)自己的開發(fā)習慣引入。
異常處理
異常處理的大致流程主要如下。
異常信息拋出 -> ControllerAdvice進行捕獲格式化輸出內(nèi)容手動拋出 CustomException并傳入ReulstEnum——> 進行捕獲錯誤信息輸出錯誤信息。
自定義異常
@Data
@EqualsAndHashCode(callSuper?=?false)
public?class?CustomException?extends?RuntimeException?{
????/**
?????*?狀態(tài)碼
?????*/
????private?final?Integer?code;
????/**
?????*?方法名稱
?????*/
????private?final?String?method;
????/**
?????*?自定義異常
?????*
?????*?@param?resultEnum?返回枚舉對象
?????*?@param?method?????方法
?????*/
????public?CustomException(ResultEnum?resultEnum,?String?method)?{
????????super(resultEnum.getMsg());
????????this.code?=?resultEnum.getCode();
????????this.method?=?method;
????}
????/**
?????*?@param?code????狀態(tài)碼
?????*?@param?message?錯誤信息
?????*?@param?method??方法
?????*/
????public?CustomException(Integer?code,?String?message,?String?method)?{
????????super(message);
????????this.code?=?code;
????????this.method?=?method;
????}
}
錯誤信息枚舉
根據(jù)業(yè)務(wù)進行添加。
@Getter
public?enum?ResultEnum?{
????/**
?????*?未知異常
?????*/
????UNKNOWN_EXCEPTION(100,?"未知異常"),
????/**
?????*?添加失敗
?????*/
????ADD_ERROR(103,?"添加失敗"),
????/**
?????*?更新失敗
?????*/
????UPDATE_ERROR(104,?"更新失敗"),
????/**
?????*?刪除失敗
?????*/
????DELETE_ERROR(105,?"刪除失敗"),
????/**
?????*?查找失敗
?????*/
????GET_ERROR(106,?"查找失敗"),
????;
????private?Integer?code;
????private?String?msg;
????ResultEnum(Integer?code,?String?msg)?{
????????this.code?=?code;
????????this.msg?=?msg;
????}
????/**
?????*?通過狀態(tài)碼獲取枚舉對象
?????*?@param?code?狀態(tài)碼
?????*?@return?枚舉對象
?????*/
????public?static?ResultEnum?getByCode(int?code){
????????for?(ResultEnum?resultEnum?:?ResultEnum.values())?{
????????????if(code?==?resultEnum.getCode()){
????????????????return?resultEnum;
????????????}
????????}
????????return?null;
????}
}
全局異常攔截
全局異常攔截是使用@ControllerAdvice進行實現(xiàn),常用的異常攔截配置可以查看 GlobalExceptionHandling。
@Slf4j
@RestControllerAdvice
public?class?GlobalExceptionHandling?{
????/**
?????*?自定義異常
?????*/
????@ExceptionHandler(value?=?CustomException.class)
????public?ResultVo?processException(CustomException?e)?{
????????log.error("位置:{}?->?錯誤信息:{}",?e.getMethod()?,e.getLocalizedMessage());
????????return?ResultVoUtil.error(Objects.requireNonNull(ResultEnum.getByCode(e.getCode())));
????}
????/**
?????*?通用異常
?????*/
????@ResponseStatus(HttpStatus.OK)
????@ExceptionHandler(Exception.class)
????public?ResultVo?exception(Exception?e)?{
????????e.printStackTrace();
????????return?ResultVoUtil.error(ResultEnum.UNKNOWN_EXCEPTION);
????}
}
案例
Controller
/**
?*?刪除用戶
?*?@param?id?用戶編號
?*?@return?成功或者失敗
?*/
@ApiOperation("刪除用戶")
@DeleteMapping("/deleteUser/{id}")
public?ResultVo?deleteUser(@PathVariable("id")?String?id){
????userService.deleteUser(id);
????return?ResultVoUtil.success();
}
Service
/**
?*?刪除用戶
?*?@param?id?id
?*/
@Override
public?void?deleteUser(String?id)?{
????//?如果刪除失敗拋出異常。?--?演示而已不推薦這樣干
????if(!removeById(id)){
????????throw?new?CustomException(ResultEnum.DELETE_ERROR,?MethodUtil.getLineInfo());
????}
}
結(jié)果


將報錯代碼所在的文件第多少行都打印出來。方便排查。
注意的點
所有手動拋出的錯誤信息,都應(yīng)在錯誤信息枚舉ResultEnum進行統(tǒng)一維護。不同的業(yè)務(wù)使用不同的錯誤碼。方便在報錯時進行分辨。快速定位問題。
多環(huán)境配置
SpringBoot多環(huán)境配置
對于一個項目來講基本都4有個環(huán)境dev,test,pre,prod,對于SpringBoot項目多建立幾個配置文件就可以了。然后啟動的時候可以通過配置spring.profiles.active 來選擇啟動的環(huán)境。

java?-jar?BasicProject.jar?--spring.profiles.active=prod
Maven多環(huán)境配置
假如想在打包的時候動態(tài)指定環(huán)境,這個時候就需要借助Maven的xml來實現(xiàn)。
配置XML
<profiles>
????<profile>
????????
????????<id>devid>
????????<activation>
????????????<activeByDefault>trueactiveByDefault>
????????activation>
????????<properties>
????????????<activatedProperties>devactivatedProperties>
????????properties>
????profile>
????<profile>
????????
????????<id>testid>
????????<properties>
????????????<activatedProperties>testactivatedProperties>
????????properties>
????profile>
????<profile>
????????
????????<id>preid>
????????<properties>
????????????<activatedProperties>preactivatedProperties>
????????properties>
????profile>
????<profile>
????????
????????<id>prodid>
????????<properties>
????????????<activatedProperties>prodactivatedProperties>
????????properties>
????profile>
profiles>
更改application.yml
spring:
??profiles:
????#?選擇環(huán)境
????active:?@activatedProperties@
使用案例
mvn?clean?package?-P?prod
mvn?clean?package?-P?pre
mvn?clean?package?-P?test
打包完可以解壓開查看application.yml 會發(fā)現(xiàn)spring.profiles.active=@activatedProperties@ 發(fā)生了改變。
日志配置
采用logback日志配置
JenkinsFile
JenkinsFile肯定顧名思義是給jenkins用的。主要是配置項目根據(jù)如何進行構(gòu)建并發(fā)布到不同的環(huán)境。需要去了解pipeline語法,以及如何配置jenkins。JenkinsFileDemo
代碼地址
https://gitee.com/huangxunhui/basic_project.git
結(jié)尾
如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,也可以隨手點個關(guān)注哦,謝謝。
END
有熱門推薦?
1.?實戰(zhàn)篇:一個核心系統(tǒng) 3 萬多行代碼的重構(gòu)之旅
2.?IntelliJ IDEA 超全優(yōu)化設(shè)置,效率杠杠的!
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點“在看”,關(guān)注公眾號并回復?Java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。
謝謝支持喲 (*^__^*)

