Spring Boot 項(xiàng)目模板,讓你少些代碼

建立一個(gè)全新的項(xiàng)目古胆,或者把舊的龐大的項(xiàng)目筛璧,進(jìn)行拆分成多個(gè)項(xiàng)目夭谤。在建立新的項(xiàng)目中,經(jīng)常需要做一些重復(fù)的工作陨帆,比如說(shuō)拷貝一下常用的工具類,通用代碼等等承二。所以就可以做一個(gè)基礎(chǔ)的項(xiàng)目方便使用亥鸠,在經(jīng)歷新項(xiàng)目的時(shí)候识啦,直接在基礎(chǔ)項(xiàng)目上進(jìn)行簡(jiǎn)單配置就可以開發(fā)業(yè)務(wù)代碼了颓哮。

基礎(chǔ)項(xiàng)目該包含哪些東西:

Swagger在線接口文檔。

CodeGenerator 代碼生成器伤极。

統(tǒng)一返回哨坪。

通用的分頁(yè)對(duì)象乍楚。

常用工具類徒溪。

全局異常攔截。

錯(cuò)誤枚舉牵舱。

自定義異常。

多環(huán)境配置文件礁凡。

Maven多環(huán)境配置顷牌。

日志配置。

JenkinsFile罪裹。

Swagger

寫接口文檔通常是一件比較頭疼的事情状共,然而swagger就用是用來(lái)幫我們解決這個(gè)問(wèn)題的》肱郏可以在線生成接口文檔碾牌,并且可以在頁(yè)面上進(jìn)行測(cè)試舶吗。

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

注意的點(diǎn):接口文檔只能在測(cè)試/開發(fā)環(huán)境開啟,其他環(huán)境請(qǐng)關(guān)閉筐带。

常用的Swagger注解

@Api用于Controller

@ApiOperation用于Controller內(nèi)的方法今穿。

@ApiResponses用于標(biāo)識(shí)接口返回?cái)?shù)據(jù)的類型。

@ApiModel用于標(biāo)識(shí)類的名稱

@ApiModelProperty用于標(biāo)識(shí)屬性的名稱

案例

@RestController

@Api(tags =?"用戶")

@AllArgsConstructor

@RequestMapping("/user")

public class UserController {

privateIUserServiceuserService;

@ApiOperation("獲取用戶列表")

@GetMapping("/listUser")

@ApiResponses(

@ApiResponse(code =?200, message =?"操作成功", response = UserVo.class)

)

publicResultVolistUser(@ValidatedListUserForm listUserForm){

returnResultVoUtil.success(userService.listUser(listUserForm));

}

}

@Data

@ApiModel("獲取用戶列表需要的表單數(shù)據(jù)")

@EqualsAndHashCode(callSuper = false)

public class ListUserForm extends PageForm {

@ApiModelProperty("用戶狀態(tài)")

@NotEmpty(message =?"用戶狀態(tài)不能為空")

@Range(min = -1, max =?1, message =?"用戶狀態(tài)有誤")

private String status;

}

對(duì)應(yīng)的swagger的配置可以查看基礎(chǔ)項(xiàng)目?jī)?nèi)的SwaggerConfiguration.java.

CodeGenerator代碼生成器伦籍。

mybatis_plus代碼生成器可以幫我們生成

entity,service,serviceImpl,mapper,mapper.xml蓝晒。省去了建立一大堆實(shí)體類的麻煩。

由于配置太長(zhǎng)這里就不貼出來(lái)了帖鸦,對(duì)應(yīng)的CodeGenerator的配置可以查看基礎(chǔ)項(xiàng)目?jī)?nèi)的CodeGenerator.java.

常用的封裝

統(tǒng)一返回 ResultVo

將所有的接口的響應(yīng)數(shù)據(jù)的格式進(jìn)行統(tǒng)一。

@Data

@ApiModel("固定返回格式")

public class ResultVo {

@ApiModelProperty("錯(cuò)誤碼")

private Integer code;

@ApiModelProperty("提示信息")

private String message;

@ApiModelProperty("響應(yīng)數(shù)據(jù)")

private Object data;

}

抽象表單 BaseForm

publicabstractclassBaseForm?{

publicabstractT?buildEntity();

}

有小伙伴可能有疑問(wèn)了作儿,這個(gè)類有啥用呢洛二。先看一下,下面的代碼攻锰。

@Override

publicbooleanaddUser(AddUserForm userForm){

User user =?newUser();

user.setNickname(userForm.getNickname());

user.setBirthday(userForm.getBirthday());

user.setUsername(userForm.getUsername());

user.setPassword(userForm.getPassword());

returnsave(user);

}

重構(gòu)一下晾嘶,感覺清爽了一些。

@Override

publicbooleanaddUser(AddUserForm userForm){

User user =?newUser();

BeanUtils.copyProperties(this,user);

returnsave(user);

}

使用BaseForm進(jìn)行重構(gòu) AddUserForm 繼承 BaseForm并重寫buildEntity

@Data

@EqualsAndHashCode(callSuper =?false)

publicclassAddUserForm?extendsBaseForm {

privateStringnickname;

privateDatebirthday;

privateStringusername;

privateStringpassword;

@Override

publicUser buildEntity() {

User user =?newUser();

BeanUtils.copyProperties(this,user);

returnuser;

}

}

@Override

publicbooleanaddUser(AddUserForm userForm){

returnsave(userForm.buildEntity());

}

上面的代碼有沒有種似曾相識(shí)的感覺娶吞,很多情況都是將接受到的參數(shù)垒迂,轉(zhuǎn)變成對(duì)應(yīng)的實(shí)體類然后保存或者更新。所以對(duì)于這類的form可以繼承baseform并實(shí)現(xiàn)buildEntity()這樣可以更加符合面向?qū)ο蠖噬撸瑂ervice不需要關(guān)心form如何轉(zhuǎn)變成entity,只需要在使用的時(shí)候調(diào)用buildEntity()即可机断,尤其是在form?->?entity相對(duì)復(fù)雜的時(shí)候楷拳,這樣做可以減少service內(nèi)的代碼。讓代碼邏輯看起來(lái)更加清晰吏奸。

通用的分頁(yè)對(duì)象

涉及到查詢的時(shí)候欢揖,絕大多數(shù)都需要用到分頁(yè),所以說(shuō)封裝分頁(yè)對(duì)象就很有必要苦丁〗牵可以注意下PageForm.calcCurrent()、PageVo.setCurrentAndSize()旺拉、PageVo.setTotal()這個(gè)幾個(gè)方法产上。

PageForm

@Data

@ApiModel(value =?"分頁(yè)數(shù)據(jù)", description =?"分頁(yè)需要的表單數(shù)據(jù)")

publicclassPageForm>{

@ApiModelProperty(value =?"頁(yè)碼 從第一頁(yè)開始 1")

@Min(value =?1, message =?"頁(yè)碼輸入有誤")

privateInteger current;

@ApiModelProperty(value =?"每頁(yè)顯示的數(shù)量 范圍在1~100")

@Range(min =?1, max =?100, message =?"每頁(yè)顯示的數(shù)量輸入有誤")

privateInteger size;

@ApiModelProperty(hidden =?true)

publicT?calcCurrent(){

current = (current -?1) * size;

return(T)?this;

}

}

PageVo

@Data

publicclassPageVo?{

@ApiModelProperty(value =?"分頁(yè)數(shù)據(jù)")

privateList records;

@ApiModelProperty(value =?"總條數(shù)")

privateInteger total;

@ApiModelProperty(value =?"總頁(yè)數(shù)")

privateInteger pages;

@ApiModelProperty(value =?"當(dāng)前頁(yè)")

privateInteger current;

@ApiModelProperty(value =?"查詢數(shù)量")

privateInteger size;

@ApiModelProperty(hidden =?true)

publicPageVo?setCurrentAndSize(PageForm<?> pageForm){

BeanUtils.copyProperties(pageForm,this);

returnthis;

}

@ApiModelProperty(hidden =?true)

publicvoidsetTotal(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 {

@ApiModelProperty("用戶狀態(tài)")

@NotEmpty(message =?"用戶狀態(tài)不能為空")

@Range(min = -1, max =?1, message =?"用戶狀態(tài)有誤")

private String status;

}

UserServiceImpl

@Override

publicPageVo?listUser(ListUserForm listUserForm){

PageVo pageVo =?newPageVo().setCurrentAndSize(listUserForm);

pageVo.setTotal(countUser(listUserForm.getStatus()));

pageVo.setRecords(userMapper.listUser(listUserForm.calcCurrent()));

returnpageVo;

}

privateInteger?countUser(String status){

returncount(newQueryWrapper().eq("status",status));

}

UserController

@ApiOperation("獲取用戶列表")

@GetMapping("/listUser")

@ApiResponses(

@ApiResponse(code =?200, message =?"操作成功", response = UserVo.class)

)

public ResultVo listUser(@ValidatedListUserForm listUserForm){

returnResultVoUtil.success(userService.listUser(listUserForm));

}

注意的點(diǎn)

PageVo在實(shí)例化的時(shí)候需要設(shè)置當(dāng)前頁(yè)每頁(yè)顯示的數(shù)量?可以調(diào)用setCurrentAndSize()完成。

進(jìn)行分頁(yè)查詢的時(shí)候蛾狗,需要計(jì)算偏移量晋涣。listUserForm.calcCurrent()

為什么要計(jì)算偏移量呢?

假如查詢第1頁(yè)每頁(yè)顯示10條記錄沉桌,前端傳遞過(guò)來(lái)的參數(shù)是current=1&amp;&amp;size=10谢鹊,這個(gè)時(shí)候limit 1,10沒有問(wèn)題。

假如查詢第2頁(yè)每頁(yè)顯示10條記錄留凭,前端傳遞過(guò)來(lái)的參數(shù)是current=2&amp;&amp;size=10佃扼,這個(gè)時(shí)候limit 2,10就有問(wèn)題,實(shí)際應(yīng)該是limit 10,10蔼夜。calcCurrent()的作用就是如此兼耀。

為什么不用MybatisPlus自帶的分頁(yè)插件呢??

自帶的分頁(yè)查詢?cè)诖罅繑?shù)據(jù)下求冷,會(huì)出現(xiàn)性能問(wèn)題瘤运。

常用工具類

常用工具類可以根據(jù)自己的開發(fā)習(xí)慣引入。

異常處理

異常處理的大致流程主要如下匠题。

異常信息拋出 ->?ControllerAdvice?進(jìn)行捕獲格式化輸出內(nèi)容

手動(dòng)拋出CustomException并傳入ReulstEnum?——> 進(jìn)行捕獲錯(cuò)誤信息輸出錯(cuò)誤信息拯坟。

自定義異常

@Data

@EqualsAndHashCode(callSuper =?false)

publicclassCustomExceptionextendsRuntimeException{

privatefinalInteger code;

privatefinalString method;

publicCustomException(ResultEnum resultEnum, String method){

super(resultEnum.getMsg());

this.code = resultEnum.getCode();

this.method = method;

}

publicCustomException(Integer code, String message, String method){

super(message);

this.code = code;

this.method = method;

}

}

錯(cuò)誤信息枚舉

根據(jù)業(yè)務(wù)進(jìn)行添加。

@Getter

publicenumResultEnum {

UNKNOWN_EXCEPTION(100,?"未知異常"),

ADD_ERROR(103,?"添加失敗"),

UPDATE_ERROR(104,?"更新失敗"),

DELETE_ERROR(105,?"刪除失敗"),

GET_ERROR(106,?"查找失敗"),

;

privateInteger code;

privateStringmsg;

ResultEnum(Integer code,?Stringmsg) {

this.code = code;

this.msg = msg;

}

publicstaticResultEnum getByCode(int code){

for(ResultEnum resultEnum : ResultEnum.values()) {

if(code == resultEnum.getCode()){

returnresultEnum;

}

}

returnnull;

}

}

全局異常攔截

全局異常攔截是使用@ControllerAdvice進(jìn)行實(shí)現(xiàn)韭山,常用的異常攔截配置可以查看 GlobalExceptionHandling郁季。

@Slf4j

@RestControllerAdvice

public class GlobalExceptionHandling {

@ExceptionHandler(value= CustomException.class)

public ResultVo processException(CustomException e) {

log.error("位置:{}?-> 錯(cuò)誤信息:{}",?e.getMethod() ,e.getLocalizedMessage());

returnResultVoUtil.error(Objects.requireNonNull(ResultEnum.getByCode(e.getCode())));

}

@ResponseStatus(HttpStatus.OK)

@ExceptionHandler(Exception.class)

public ResultVo exception(Exception e) {

e.printStackTrace();

returnResultVoUtil.error(ResultEnum.UNKNOWN_EXCEPTION);

}

}

案例

Controller

@ApiOperation("刪除用戶")

@DeleteMapping("/deleteUser/{id}")

public ResultVo deleteUser(@PathVariable("id") String id){

userService.deleteUser(id);

returnResultVoUtil.success();

}

Service

@Override

publicvoiddeleteUser(Stringid) {

if(!removeById(id)){

thrownewCustomException(ResultEnum.DELETE_ERROR, MethodUtil.getLineInfo());

}

}

結(jié)果

將報(bào)錯(cuò)代碼所在的文件第多少行都打印出來(lái)。方便排查钱磅。

注意的點(diǎn)

所有手動(dòng)拋出的錯(cuò)誤信息巩踏,都應(yīng)在錯(cuò)誤信息枚舉ResultEnum進(jìn)行統(tǒng)一維護(hù)。不同的業(yè)務(wù)使用不同的錯(cuò)誤碼续搀。方便在報(bào)錯(cuò)時(shí)進(jìn)行分辨〔ぞ唬快速定位問(wèn)題禁舷。

多環(huán)境配置

SpringBoot多環(huán)境配置

對(duì)于一個(gè)項(xiàng)目來(lái)講基本都4有個(gè)環(huán)境dev,test,pre,prod彪杉,對(duì)于SpringBoot項(xiàng)目多建立幾個(gè)配置文件就可以了。然后啟動(dòng)的時(shí)候可以通過(guò)配置spring.profiles.active?來(lái)選擇啟動(dòng)的環(huán)境牵咙。

java?-jar?BasicProject.jar?--spring.profiles.active=prod

Maven多環(huán)境配置

假如想在打包的時(shí)候動(dòng)態(tài)指定環(huán)境派近,這個(gè)時(shí)候就需要借助Maven的xml來(lái)實(shí)現(xiàn)。

配置XML

dev

true

dev

test

test

pre

pre

prod

prod

更改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?會(huì)發(fā)現(xiàn)spring.profiles.active=@activatedProperties@?發(fā)生了改變洁桌。

日志配置

采用logback日志配置

JenkinsFile

JenkinsFile肯定顧名思義是給jenkins用的渴丸。主要是配置項(xiàng)目根據(jù)如何進(jìn)行構(gòu)建并發(fā)布到不同的環(huán)境。需要去了解pipeline語(yǔ)法另凌,以及如何配置jenkins谱轨。

JenkinsFileDemo?https://gitee.com/huangxunhui/basic_project/blob/master/Jenkinsfile

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吠谢,隨后出現(xiàn)的幾起案子土童,更是在濱河造成了極大的恐慌,老刑警劉巖工坊,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件献汗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡王污,警方通過(guò)查閱死者的電腦和手機(jī)罢吃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昭齐,“玉大人尿招,你說(shuō)我怎么就攤上這事∷纠耍” “怎么了泊业?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)啊易。 經(jīng)常有香客問(wèn)我吁伺,道長(zhǎng),這世上最難降的妖魔是什么租谈? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任篮奄,我火速辦了婚禮,結(jié)果婚禮上割去,老公的妹妹穿的比我還像新娘窟却。我一直安慰自己,他們只是感情好呻逆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布夸赫。 她就那樣靜靜地躺著,像睡著了一般咖城。 火紅的嫁衣襯著肌膚如雪茬腿。 梳的紋絲不亂的頭發(fā)上呼奢,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音切平,去河邊找鬼握础。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悴品,可吹牛的內(nèi)容都是我干的禀综。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼苔严,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼定枷!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起邦蜜,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤依鸥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后悼沈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贱迟,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年絮供,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衣吠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壤靶,死狀恐怖缚俏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贮乳,我是刑警寧澤忧换,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站向拆,受9級(jí)特大地震影響亚茬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浓恳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一刹缝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颈将,春花似錦梢夯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春沾凄,著一層夾襖步出監(jiān)牢的瞬間梗醇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工撒蟀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人温鸽。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓保屯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涤垫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姑尺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容