SpringBoot專案骨架
前言
建立一個全新的專案,或者把舊的龐大的專案,進行拆分成多個專案。在建立新的專案中,經常需要做一些重複的工作,比如說拷貝一下常用的工具類,通用程式碼等等。
所以就可以做一個基礎的專案方便使用,在經歷新專案的時候,直接在基礎專案上進行簡單配置就可以開發業務程式碼了。
基礎專案該包含哪些東西。
-
Swagger線上介面文件。
-
CodeGenerator 程式碼生成器。
-
統一返回。
-
通用的分頁物件。
-
常用工具類。
-
全域性異常攔截。
-
錯誤列舉。
-
自定義異常。
-
多環境配置檔案。
-
Maven多環境配置。
-
日誌配置。
-
JenkinsFile。
❝可以在評論區進行補充
❞
Swagger
寫介面文件通常是一件比較頭疼的事情,然而swagger就用是用來幫我們解決這個問題的。可以線上生成介面文件,並且可以在頁面上進行測試。
可以非常清楚的顯示,請求資料已經響應資料。當然這一切都需要在程式碼中進行配置。
「注意的點:介面文件只能在測試/開發環境開啟,其他環境請關閉。」
常用的Swagger註解
-
@Api用於Controller
-
@ApiOperation用於Controller內的方法。
-
@ApiResponses用於標識介面返回資料的型別。
-
@ApiModel用於標識類的名稱
-
@ApiModelProperty用於標識屬性的名稱
案例
@RestController
@Api(tags="使用者")
@AllArgsConstructor
@RequestMapping("/user")
publicclassUserController{
privateIUserServiceuserService;
/**
*獲取使用者列表
*@paramlistUserForm表單資料
*@return使用者列表
*/
@ApiOperation("獲取使用者列表")
@GetMapping("/listUser")
@ApiResponses(
@ApiResponse(code=200,message="操作成功",response=UserVo.class)
)
publicResultVolistUser(@ValidatedListUserFormlistUserForm){
returnResultVoUtil.success(userService.listUser(listUserForm));
}
}
@Data
@ApiModel("獲取使用者列表需要的表單資料")
@EqualsAndHashCode(callSuper=false)
publicclassListUserFormextendsPageForm<ListUserForm>{
/**
*使用者狀態
*/
@ApiModelProperty("使用者狀態")
@NotEmpty(message="使用者狀態不能為空")
@Range(min=-1,max=1,message="使用者狀態有誤")
privateStringstatus;
}
對應的swagger的配置可以檢視基礎專案內的SwaggerConfiguration.java
.
CodeGenerator程式碼生成器。
mybatis_plus程式碼生成器可以幫我們生成entity
,service
,serviceImpl
,mapper
,mapper.xml
。省去了建立一大堆實體類的麻煩。
由於配置太長這裡就不貼出來了,對應的CodeGenerator的配置可以檢視基礎專案內的CodeGenerator.java
.
常用的封裝
統一返回 ResultVo
將所有的介面的響應資料的格式進行統一。
@Data
@ApiModel("固定返回格式")
publicclassResultVo{
/**
*錯誤碼
*/
@ApiModelProperty("錯誤碼")
privateIntegercode;
/**
*提示資訊
*/
@ApiModelProperty("提示資訊")
privateStringmessage;
/**
*具體的內容
*/
@ApiModelProperty("響應資料")
privateObjectdata;
}
抽象表單 BaseForm
publicabstractclassBaseForm<T>{
/**
*獲取例項
*@return返回實體類
*/
publicabstractTbuildEntity();
}
有小夥伴可能有疑問了,這個類有啥用呢。先看一下,下面的程式碼。
/**
*新增使用者
*@paramuserForm表單資料
*@returntrue或者false
*/
@Override
publicbooleanaddUser(AddUserFormuserForm){
Useruser=newUser();
user.setNickname(userForm.getNickname());
user.setBirthday(userForm.getBirthday());
user.setUsername(userForm.getUsername());
user.setPassword(userForm.getPassword());
returnsave(user);
}
重構一下,感覺清爽了一些。
/**
*新增使用者
*@paramuserForm表單資料
*@returntrue或者false
*/
@Override
publicbooleanaddUser(AddUserFormuserForm){
Useruser=newUser();
BeanUtils.copyProperties(this,user);
returnsave(user);
}
使用BaseForm進行重構 AddUserForm 繼承 BaseForm並重寫buildEntity
@Data
@EqualsAndHashCode(callSuper=false)
publicclassAddUserFormextendsBaseForm<User>{
/**
*暱稱
*/
privateStringnickname;
/**
*生日
*/
privateDatebirthday;
/**
*使用者名稱
*/
privateStringusername;
/**
*密碼
*/
privateStringpassword;
/**
*構造實體
*@return實體物件
*/
@Override
publicUserbuildEntity(){
Useruser=newUser();
BeanUtils.copyProperties(this,user);
returnuser;
}
}
/**
*新增使用者
*@paramuserForm表單資料
*@returntrue或者false
*/
@Override
publicbooleanaddUser(AddUserFormuserForm){
returnsave(userForm.buildEntity());
}
上面的程式碼有沒有種似曾相識的感覺,很多情況都是將接受到的引數,轉變成對應的實體類然後「儲存」或者「更新」。
所以對於這類的form
可以繼承baseform
並實現buildEntity()
這樣可以更加符合面向物件,service
不需要關心form
如何轉變成entity
,只需要在使用的時候呼叫buildEntity()
即可,尤其是在form
->entity
相對複雜的時候,這樣做可以減少service
內的程式碼。讓程式碼邏輯看起來更加清晰。
通用的分頁物件
涉及到查詢的時候,絕大多數都需要用到分頁,所以說封裝分頁物件就很有必要。可以注意下PageForm.calcCurrent()
、PageVo.setCurrentAndSize()
、PageVo.setTotal()
這個幾個方法。
PageForm
@Data
@ApiModel(value="分頁資料",description="分頁需要的表單資料")
publicclassPageForm<TextendsPageForm<?>>{
/**
*頁碼
*/
@ApiModelProperty(value="頁碼從第一頁開始1")
@Min(value=1,message="頁碼輸入有誤")
privateIntegercurrent;
/**
*每頁顯示的數量
*/
@ApiModelProperty(value="每頁顯示的數量範圍在1~100")
@Range(min=1,max=100,message="每頁顯示的數量輸入有誤")
privateIntegersize;
/**
*計算當前頁,方便mysql進行分頁查詢
*@return返回pageForm
*/
@ApiModelProperty(hidden=true)
publicTcalcCurrent(){
current=(current-1)*size;
return(T)this;
}
}
PageVo
@Data
publicclassPageVo<T>{
/**
*分頁資料
*/
@ApiModelProperty(value="分頁資料")
privateList<T>records;
/**
*總條數
*/
@ApiModelProperty(value="總條數")
privateIntegertotal;
/**
*總頁數
*/
@ApiModelProperty(value="總頁數")
privateIntegerpages;
/**
*當前頁
*/
@ApiModelProperty(value="當前頁")
privateIntegercurrent;
/**
*查詢數量
*/
@ApiModelProperty(value="查詢數量")
privateIntegersize;
/**
*設定當前頁和每頁顯示的數量
*@parampageForm分頁表單
*@return返回分頁資訊
*/
@ApiModelProperty(hidden=true)
publicPageVo<T>setCurrentAndSize(PageForm<?>pageForm){
BeanUtils.copyProperties(pageForm,this);
returnthis;
}
/**
*設定總記錄數
*@paramtotal總記錄數
*/
@ApiModelProperty(hidden=true)
publicvoidsetTotal(Integertotal){
this.total=total;
this.setPages(this.total%this.size>0?this.total/this.size+1:this.total/this.size);
}
}
案例
ListUserForm
@Data
@ApiModel("獲取使用者列表需要的表單資料")
@EqualsAndHashCode(callSuper=false)
publicclassListUserFormextendsPageForm<ListUserForm>{
/**
*使用者狀態
*/
@ApiModelProperty("使用者狀態")
@NotEmpty(message="使用者狀態不能為空")
@Range(min=-1,max=1,message="使用者狀態有誤")
privateStringstatus;
}
UserServiceImpl
/**
*獲取使用者列表
*@paramlistUserForm表單資料
*@return使用者列表
*/
@Override
publicPageVo<UserVo>listUser(ListUserFormlistUserForm){
PageVo<UserVo>pageVo=newPageVo<UserVo>().setCurrentAndSize(listUserForm);
pageVo.setTotal(countUser(listUserForm.getStatus()));
pageVo.setRecords(userMapper.listUser(listUserForm.calcCurrent()));
returnpageVo;
}
/**
*獲取使用者數量
*@paramstatus狀態
*@return使用者數量
*/
privateIntegercountUser(Stringstatus){
returncount(newQueryWrapper<User>().eq("status",status));
}
UserController
/**
*獲取使用者列表
*@paramlistUserForm表單資料
*@return使用者列表
*/
@ApiOperation("獲取使用者列表")
@GetMapping("/listUser")
@ApiResponses(
@ApiResponse(code=200,message="操作成功",response=UserVo.class)
)
publicResultVolistUser(@ValidatedListUserFormlistUserForm){
returnResultVoUtil.success(userService.listUser(listUserForm));
}
注意的點
-
PageVo在例項化的時候需要設定「當前頁」和「每頁顯示的數量」可以呼叫
setCurrentAndSize()
完成。 -
進行分頁查詢的時候,需要計算偏移量。
listUserForm.calcCurrent()
為什麼要計算偏移量呢?
-
假如查詢第1頁每頁顯示10條記錄,前端傳遞過來的引數是
current=1&&size=10
,這個時候limit 1,10
沒有問題。 -
假如查詢第2頁每頁顯示10條記錄,前端傳遞過來的引數是
current=2&&size=10
,這個時候limit 2,10
就有問題,實際應該是limit 10,10
。calcCurrent()的作用就是如此
。
為什麼不用MybatisPlus自帶的分頁外掛呢?
❝自帶的分頁查詢在大量資料下,會出現效能問題。
❞
常用工具類
常用工具類可以根據自己的開發習慣引入。
異常處理
異常處理的大致流程主要如下。
-
異常資訊丟擲 ->
ControllerAdvice
進行捕獲格式化輸出內容 -
手動丟擲
CustomException
並傳入ReulstEnum
——> 進行捕獲錯誤資訊輸出錯誤資訊。
自定義異常
@Data
@EqualsAndHashCode(callSuper=false)
publicclassCustomExceptionextendsRuntimeException{
/**
*狀態碼
*/
privatefinalIntegercode;
/**
*方法名稱
*/
privatefinalStringmethod;
/**
*自定義異常
*
*@paramresultEnum返回列舉物件
*@parammethod方法
*/
publicCustomException(ResultEnumresultEnum,Stringmethod){
super(resultEnum.getMsg());
this.code=resultEnum.getCode();
this.method=method;
}
/**
*@paramcode狀態碼
*@parammessage錯誤資訊
*@parammethod方法
*/
publicCustomException(Integercode,Stringmessage,Stringmethod){
super(message);
this.code=code;
this.method=method;
}
}
錯誤資訊列舉
根據業務進行新增。
@Getter
publicenumResultEnum{
/**
*未知異常
*/
UNKNOWN_EXCEPTION(100,"未知異常"),
/**
*新增失敗
*/
ADD_ERROR(103,"新增失敗"),
/**
*更新失敗
*/
UPDATE_ERROR(104,"更新失敗"),
/**
*刪除失敗
*/
DELETE_ERROR(105,"刪除失敗"),
/**
*查詢失敗
*/
GET_ERROR(106,"查詢失敗"),
;
privateIntegercode;
privateStringmsg;
ResultEnum(Integercode,Stringmsg){
this.code=code;
this.msg=msg;
}
/**
*通過狀態碼獲取列舉物件
*@paramcode狀態碼
*@return列舉物件
*/
publicstaticResultEnumgetByCode(intcode){
for(ResultEnumresultEnum:ResultEnum.values()){
if(code==resultEnum.getCode()){
returnresultEnum;
}
}
returnnull;
}
}
全域性異常攔截
全域性異常攔截是使用@ControllerAdvice
進行實現,常用的異常攔截配置可以檢視 GlobalExceptionHandling。
@Slf4j
@RestControllerAdvice
publicclassGlobalExceptionHandling{
/**
*自定義異常
*/
@ExceptionHandler(value=CustomException.class)
publicResultVoprocessException(CustomExceptione){
log.error("位置:{}->錯誤資訊:{}",e.getMethod(),e.getLocalizedMessage());
returnResultVoUtil.error(Objects.requireNonNull(ResultEnum.getByCode(e.getCode())));
}
/**
*通用異常
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(Exception.class)
publicResultVoexception(Exceptione){
e.printStackTrace();
returnResultVoUtil.error(ResultEnum.UNKNOWN_EXCEPTION);
}
}
案例
Controller
/**
*刪除使用者
*@paramid使用者編號
*@return成功或者失敗
*/
@ApiOperation("刪除使用者")
@DeleteMapping("/deleteUser/{id}")
publicResultVodeleteUser(@PathVariable("id")Stringid){
userService.deleteUser(id);
returnResultVoUtil.success();
}
Service
/**
*刪除使用者
*@paramidid
*/
@Override
publicvoiddeleteUser(Stringid){
//如果刪除失敗丟擲異常。--演示而已不推薦這樣幹
if(!removeById(id)){
thrownewCustomException(ResultEnum.DELETE_ERROR,MethodUtil.getLineInfo());
}
}
結果
「將報錯程式碼所在的檔案第多少行都打印出來。方便排查。」
注意的點
所有手動丟擲的錯誤資訊,都應在錯誤資訊列舉ResultEnum
進行統一維護。不同的業務使用不同的錯誤碼。方便在報錯時進行分辨。快速定位問題。
多環境配置
SpringBoot多環境配置
對於一個專案來講基本都4有個環境dev
,test
,pre
,prod
,對於SpringBoot專案多建立幾個配置檔案就可以了。
然後啟動的時候可以通過配置spring.profiles.active
來選擇啟動的環境。
java-jarBasicProject.jar--spring.profiles.active=prod
Maven多環境配置
假如想在打包的時候動態指定環境,這個時候就需要藉助Maven的xml來實現。
配置XML
<!--配置環境-->
<profiles>
<profile>
<!--開發-->
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<activatedProperties>dev</activatedProperties>
</properties>
</profile>
<profile>
<!--測試-->
<id>test</id>
<properties>
<activatedProperties>test</activatedProperties>
</properties>
</profile>
<profile>
<!--準生產-->
<id>pre</id>
<properties>
<activatedProperties>pre</activatedProperties>
</properties>
</profile>
<profile>
<!--生產-->
<id>prod</id>
<properties>
<activatedProperties>prod</activatedProperties>
</properties>
</profile>
</profiles>
更改application.yml
spring:
profiles:
#選擇環境
active:@activatedProperties@
使用案例
mvncleanpackage-Pprod
mvncleanpackage-Ppre
mvncleanpackage-Ptest
打包完可以解壓開檢視application.yml
會發現spring.profiles.active=@activatedProperties@
發生了改變。
日誌配置
採用logback日誌配置,參考
https://gitee.com/huangxunhui/basic_project/blob/master/src/main/resources/logback-spring.xml
JenkinsFile
JenkinsFile肯定顧名思義是給jenkins用的。主要是配置專案根據如何進行構建併發布到不同的環境。需要去了解pipeline語法,以及如何配置jenkins。
結尾
如果覺得對你有幫助,可以隨手點個在看哦,謝謝。