<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 項目搭建模板

          共 16132字,需瀏覽 33分鐘

           ·

          2021-03-11 11:45

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

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

          • Swagger在線接口文檔。
          • CodeGenerator 代碼生成器。
          • 統(tǒng)一返回。
          • 通用的分頁對象。
          • 常用工具類。
          • 全局異常攔截。
          • 錯誤枚舉。
          • 自定義異常。
          • 多環(huán)境配置文件。
          • Maven多環(huán)境配置。
          • 日志配置。
          • JenkinsFile。
          可以在評論區(qū)進行補充,我把 Spring Boot 相關(guān)的文章整理成了 PDF,關(guān)注 Java后端,后臺回復(fù) 666 下載這本 Java技術(shù)棧手冊。


          Swagger

          寫接口文檔通常是一件比較頭疼的事情,然而swagger就用是用來幫我們解決這個問題的??梢栽诰€生成接口文檔,并且可以在頁面上進行測試。
          可以非常清楚的顯示,請求數(shù)據(jù)已經(jīng)響應(yīng)數(shù)據(jù)。當(dāng)然這一切都需要在代碼中進行配置。
          注意的點:接口文檔只能在測試/開發(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<ListUserForm> {
              
              @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<User> {
              
              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相對復(fù)雜的時候,這樣做可以減少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<T> records;
              @ApiModelProperty(value = "總條數(shù)")
              private Integer total;
              @ApiModelProperty(value = "總頁數(shù)")
              private Integer pages;
              @ApiModelProperty(value = "當(dāng)前頁")
              private Integer current;
              @ApiModelProperty(value = "查詢數(shù)量")
              private Integer size;
              @ApiModelProperty(hidden = true)
              public PageVo<T> 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<ListUserForm> {
              
              @ApiModelProperty("用戶狀態(tài)")
              @NotEmpty(message = "用戶狀態(tài)不能為空")
              @Range(min = -1 , max = 1 , message = "用戶狀態(tài)有誤")
              private String status;

          }


          UserServiceImpl


          @Override
          public PageVo<UserVo> listUser(ListUserForm listUserForm) {
              PageVo<UserVo> pageVo = new PageVo<UserVo>().setCurrentAndSize(listUserForm);
              pageVo.setTotal(countUser(listUserForm.getStatus()));
              pageVo.setRecords(userMapper.listUser(listUserForm.calcCurrent()));
              return pageVo;
          }

          private Integer countUser(String status){
              return count(new QueryWrapper<User>().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è)置當(dāng)前頁每頁顯示的數(shù)量 可以調(diào)用setCurrentAndSize()完成。
          • 進行分頁查詢的時候,需要計算偏移量。listUserForm.calcCurrent()
          為什么要計算偏移量呢?
          • 假如查詢第1頁每頁顯示10條記錄,前端傳遞過來的參數(shù)是current=1&amp;&amp;size=10,這個時候limit 1,10沒有問題。
          • 假如查詢第2頁每頁顯示10條記錄,前端傳遞過來的參數(shù)是current=2&amp;&amp;size=10,這個時候limit 2,10就有問題,實際應(yīng)該是limit 10,10。calcCurrent()的作用就是如此
          為什么不用MybatisPlus自帶的分頁插件呢? 
          自帶的分頁查詢在大量數(shù)據(jù)下,會出現(xiàn)性能問題。

          常用工具類

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

          異常處理

          異常處理的大致流程主要如下。
          • 異常信息拋出 -> 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>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:
              # 選擇環(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


          順便給大家推薦一個GitHub項目,這個 GitHub 整理了上千本常用技術(shù)PDF,絕大部分核心的技術(shù)書籍都可以在這里找到,

          GitHub地址:https://github.com/javadevbooks/books

          Gitee地址:https://gitee.com/javadevbooks/books

          電子書已經(jīng)更新好了,你們需要的可以自行下載了,記得點一個star,持續(xù)更新中..



          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  婷婷好色五月天 | 亚洲视频免费观看 | 东方AV在线播放 | 精品福利在线观看 | 99视频在线 |