<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一份 Spring Boot 項目搭建模板

          共 3350字,需瀏覽 7分鐘

           ·

          2020-11-25 17:38

          點擊上方藍色字體,選擇“設(shè)置星標”

          優(yōu)質(zhì)文章,第一時間送達



          前言

          建立一個全新的項目,或者把舊的龐大的項目,進行拆分成多個項目。在建立新的項目中,經(jīng)常需要做一些重復的工作,比如說拷貝一下常用的工具類,通用代碼等等。所以就可以做一個基礎(chǔ)的項目方便使用,在經(jīng)歷新項目的時候,直接在基礎(chǔ)項目上進行簡單配置就可以開發(fā)業(yè)務(wù)代碼了。

          基礎(chǔ)項目該包含哪些東西。


          • Swagger在線接口文檔。
          • CodeGenerator 代碼生成器。
          • 統(tǒng)一返回。
          • 通用的分頁對象。
          • 常用工具類。
          • 全局異常攔截。
          • 錯誤枚舉。
          • 自定義異常。
          • 多環(huán)境配置文件。
          • Maven多環(huán)境配置。
          • 日志配置。
          • JenkinsFile。


          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;

          ????
          ????@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 {
          ????
          ????@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;

          ????
          ????@ApiModelProperty("響應(yīng)數(shù)據(jù)")
          ????private Object data;

          }

          抽象表單 BaseForm

          public?abstract?class?BaseForm<T> {
          ????
          ????public?abstract?T buildEntity();

          }


          有小伙伴可能有疑問了,這個類有啥用呢。先看一下,下面的代碼。


          @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)一下,感覺清爽了一些。
          @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 {
          ????
          ????private?String?nickname;

          ????private?Date?birthday;
          ????
          ????private?String?username;
          ????
          ????private?String?password;

          ????
          ????@Override
          ????public?User buildEntity() {
          ????????User user = new?User();
          ????????BeanUtils.copyProperties(this,user);
          ????????return?user;
          ????}
          }

          @Override
          public?boolean?addUser(AddUserForm userForm)?{
          ????return?save(userForm.buildEntity());
          }

          上面的代碼有沒有種似曾相識的感覺,很多情況都是將接受到的參數(shù),轉(zhuǎn)變成對應(yīng)的實體類然后保存或者更新。所以對于這類的form可以繼承baseform并實現(xiàn)buildEntity()這樣可以更加符合面向?qū)ο螅?/span>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;

          ????
          ????@ApiModelProperty(value = "每頁顯示的數(shù)量 范圍在1~100")
          ????@Range(min = 1, max = 100, message = "每頁顯示的數(shù)量輸入有誤")
          ????private?Integer size;

          ????
          ????@ApiModelProperty(hidden = true)
          ????public?T calcCurrent(){
          ????????current = (current - 1?) * size;
          ????????return?(T) this;
          ????}
          }

          PageVo

          @Data
          public?class?PageVo<T> {
          ????@ApiModelProperty(value = "分頁數(shù)據(jù)")
          ????private?List records;
          ????@ApiModelProperty(value = "總條數(shù)")
          ????private?Integer total;
          ????@ApiModelProperty(value = "總頁數(shù)")
          ????private?Integer pages;
          ????@ApiModelProperty(value = "當前頁")
          ????private?Integer current;
          ????@ApiModelProperty(value = "查詢數(shù)量")
          ????private?Integer size;
          ????@ApiModelProperty(hidden = true)
          ????public?PageVo setCurrentAndSize(PageForm pageForm){
          ????????BeanUtils.copyProperties(pageForm,this);
          ????????return?this;
          ????}
          ????@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 {
          ????
          ????@ApiModelProperty("用戶狀態(tài)")
          ????@NotEmpty(message = "用戶狀態(tài)不能為空")
          ????@Range(min = -1?, max = 1?, message = "用戶狀態(tài)有誤")
          ????private String status;

          }


          UserServiceImpl


          @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;
          }

          private?Integer countUser(String status){
          ????return?count(new?QueryWrapper().eq("status",status));
          }


          UserController


          @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,10calcCurrent()的作用就是如此
          為什么不用?MybatisPlus?自帶的分頁插件呢??
          自帶的分頁查詢在大量數(shù)據(jù)下,會出現(xiàn)性能問題。

          常用工具類

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

          異常處理

          異常處理的大致流程主要如下。
          • 異常信息拋出 -> ControllerAdvice 進行捕獲格式化輸出內(nèi)容
          • 手動拋出CustomException并傳入ReulstEnum ——> 進行捕獲錯誤信息輸出錯誤信息。


          自定義異常


          @Data
          @EqualsAndHashCode(callSuper = false)
          public?class?CustomException?extends?RuntimeException?{
          ????
          ????private?final?Integer code;

          ????
          ????private?final?String method;

          ????
          ????public?CustomException(ResultEnum resultEnum, String method)?{
          ????????super(resultEnum.getMsg());
          ????????this.code = resultEnum.getCode();
          ????????this.method = 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;
          ????}

          ????
          ????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

          @ApiOperation("刪除用戶")
          @DeleteMapping("/deleteUser/{id}")
          public ResultVo deleteUser(@PathVariable("id") String id){
          ????userService.deleteUser(id);
          ????return?ResultVoUtil.success();
          }


          Service

          @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/blob/master/Jenkinsfile

          代碼地址

          https://gitee.com/huangxunhui/basic_project.git

          作者:?不一樣的科技宅?

          鏈接:juejin.im/post/6844904083942277127


          -END-


          springboot開發(fā)基礎(chǔ)腳手架零基礎(chǔ)到項目實戰(zhàn)?

          加我微信回復“springboot腳手架”即可獲取

          點個在看?

          謝謝支持喲 (*^__^*)

          瀏覽 41
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产高清免费操逼 | 手机免费毛片 | 爱情岛一区二区三区 | 无码三级视频在线观看 | 天天免费黄色视 |