点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

作者:不一样的科技宅 

juejin.im/post/6844904083942277127

前言

建立一个全新的项目,或者把旧的庞大的项目,进行拆分成多个项目。在建立新的项目中,经常需要做一些重复的工作,比如说拷贝一下常用的工具类,通用代码等等。

所以就可以做一个基础的项目方便使用,在经历新项目的时候,直接在基础项目上进行简单配置就可以开发业务代码了。

基础项目该包含哪些东西。

  • Swagger在线接口文档。

  • CodeGenerator 代码生成器。

  • 统一返回。

  • 通用的分页对象。

  • 常用工具类。

  • 全局异常拦截。

  • 错误枚举。

  • 自定义异常。

  • 多环境配置文件。

  • Maven多环境配置。

  • 日志配置。

  • JenkinsFile。

可以在评论区进行补充


Swagger

写接口文档通常是一件比较头疼的事情,然而swagger就用是用来帮我们解决这个问题的。可以在线生成接口文档,并且可以在页面上进行测试。

可以非常清楚的显示,请求数据已经响应数据。当然这一切都需要在代码中进行配置。

「注意的点:接口文档只能在测试/开发环境开启,其他环境请关闭。」

常用的Swagger注解

  • @Api用于Controller

  • @ApiOperation用于Controller内的方法。

  • @ApiResponses用于标识接口返回数据的类型。

  • @ApiModel用于标识类的名称

  • @ApiModelProperty用于标识属性的名称

案例

@RestController
@Api(tags = "用户")
@AllArgsConstructor
@RequestMapping("/user")
public class UserController {  private IUserService userService;  /**  * 获取用户列表  * @param listUserForm 表单数据  * @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("获取用户列表需要的表单数据")
@EqualsAndHashCode(callSuper = false)
public class ListUserForm extends PageForm<ListUserForm> {  /**  * 用户状态  */  @ApiModelProperty("用户状态")  @NotEmpty(message = "用户状态不能为空")  @Range(min =  -1 , max = 1 , message = "用户状态有误")  private String status;  }

对应的swagger的配置可以查看基础项目内的SwaggerConfiguration.java.

CodeGenerator代码生成器。

mybatis_plus代码生成器可以帮我们生成entity,service,serviceImpl,mapper,mapper.xml。省去了建立一大堆实体类的麻烦。

由于配置太长这里就不贴出来了,对应的CodeGenerator的配置可以查看基础项目内的CodeGenerator.java.


常用的封装

统一返回 ResultVo

将所有的接口的响应数据的格式进行统一。

@Data
@ApiModel("固定返回格式")
public class ResultVo {  /**  * 错误码  */  @ApiModelProperty("错误码")  private Integer code;  /**  * 提示信息  */  @ApiModelProperty("提示信息")  private String message;  /**  * 具体的内容  */  @ApiModelProperty("响应数据")  private Object data;  }

抽象表单 BaseForm

public abstract class BaseForm<T> {  /**  * 获取实例  * @return 返回实体类  */  public abstract T buildEntity();  }

有小伙伴可能有疑问了,这个类有啥用呢。先看一下,下面的代码。

/**  * 添加用户  * @param userForm 表单数据  * @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);
}

重构一下,感觉清爽了一些。

/**  * 添加用户  * @param userForm 表单数据  * @return true 或者 false  */
@Override
public boolean addUser(AddUserForm userForm) {  User user = new User();  BeanUtils.copyProperties(this,user);  return save(user);
}  

使用BaseForm进行重构 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;  /**  * 构造实体  * @return 实体对象  */  @Override  public User buildEntity() {  User user = new User();  BeanUtils.copyProperties(this,user);  return user;  }
}
/**  * 添加用户  * @param userForm 表单数据  * @return true 或者 false  */
@Override
public boolean addUser(AddUserForm userForm) {  return save(userForm.buildEntity());
}

上面的代码有没有种似曾相识的感觉,很多情况都是将接受到的参数,转变成对应的实体类然后「保存」或者「更新」

所以对于这类的form可以继承baseform并实现buildEntity()这样可以更加符合面向对象,service不需要关心form如何转变成entity,只需要在使用的时候调用buildEntity()即可,尤其是在form -> entity相对复杂的时候,这样做可以减少service内的代码。让代码逻辑看起来更加清晰。


通用的分页对象

涉及到查询的时候,绝大多数都需要用到分页,所以说封装分页对象就很有必要。可以注意下 PageForm.calcCurrent()PageVo.setCurrentAndSize()PageVo.setTotal()这个几个方法。

PageForm

@Data
@ApiModel(value = "分页数据", description = "分页需要的表单数据")
public class PageForm<T extends PageForm<?>>{  /**  * 页码  */  @ApiModelProperty(value = "页码 从第一页开始 1")  @Min(value = 1, message = "页码输入有误")  private Integer current;  /**  * 每页显示的数量  */  @ApiModelProperty(value = "每页显示的数量 范围在1~100")  @Range(min = 1, max = 100, message = "每页显示的数量输入有误")  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> {  /**  * 分页数据  */  @ApiModelProperty(value = "分页数据")  private List<T> records;  /**  * 总条数  */  @ApiModelProperty(value = "总条数")  private Integer total;  /**  * 总页数  */  @ApiModelProperty(value = "总页数")  private Integer pages;  /**  * 当前页  */  @ApiModelProperty(value = "当前页")  private Integer current;  /**  * 查询数量  */  @ApiModelProperty(value = "查询数量")  private Integer size;  /**  * 设置当前页和每页显示的数量  * @param pageForm 分页表单  * @return 返回分页信息  */  @ApiModelProperty(hidden = true)  public PageVo<T> setCurrentAndSize(PageForm<?> pageForm){  BeanUtils.copyProperties(pageForm,this);  return this;  }  /**  * 设置总记录数  * @param total 总记录数  */  @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("获取用户列表需要的表单数据")
@EqualsAndHashCode(callSuper = false)
public class ListUserForm extends PageForm<ListUserForm> {  /**  * 用户状态  */  @ApiModelProperty("用户状态")  @NotEmpty(message = "用户状态不能为空")  @Range(min =  -1 , max = 1 , message = "用户状态有误")  private String status;  }
UserServiceImpl
/**  * 获取用户列表  * @param listUserForm 表单数据  * @return 用户列表  */
@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;
}  /**  * 获取用户数量  * @param status 状态  * @return 用户数量  */
private Integer countUser(String status){  return count(new QueryWrapper<User>().eq("status",status));
}
UserController
/**  * 获取用户列表  * @param listUserForm 表单数据  * @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在实例化的时候需要设置「当前页」「每页显示的数量」 可以调用setCurrentAndSize()完成。

  • 进行分页查询的时候,需要计算偏移量。listUserForm.calcCurrent()

为什么要计算偏移量呢?

  • 假如查询第1页每页显示10条记录,前端传递过来的参数是current=1&amp;&amp;size=10,这个时候limit 1,10没有问题。

  • 假如查询第2页每页显示10条记录,前端传递过来的参数是current=2&amp;&amp;size=10,这个时候limit 2,10就有问题,实际应该是limit 10,10calcCurrent()的作用就是如此

为什么不用MybatisPlus自带的分页插件呢?

自带的分页查询在大量数据下,会出现性能问题。

常用工具类

常用工具类可以根据自己的开发习惯引入。


异常处理

异常处理的大致流程主要如下。

  • 异常信息抛出 -> ControllerAdvice 进行捕获格式化输出内容

  • 手动抛出CustomException并传入ReulstEnum ——> 进行捕获错误信息输出错误信息。

自定义异常

@Data
@EqualsAndHashCode(callSuper = false)
public class CustomException extends RuntimeException {  /**  * 状态码  */  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    状态码  * @param message 错误信息  * @param method  方法  */  public CustomException(Integer code, String message, String method) {  super(message);  this.code = code;  this.method = method;  }  }

错误信息枚举

根据业务进行添加。

@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;  }  /**  * 通过状态码获取枚举对象  * @param code 状态码  * @return 枚举对象  */  public static ResultEnum getByCode(int code){  for (ResultEnum resultEnum : ResultEnum.values()) {  if(code == resultEnum.getCode()){  return resultEnum;  }  }  return null;  }  }

全局异常拦截

全局异常拦截是使用@ControllerAdvice进行实现,常用的异常拦截配置可以查看 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());  }
}
结果

「将报错代码所在的文件第多少行都打印出来。方便排查。」

注意的点

所有手动抛出的错误信息,都应在错误信息枚举ResultEnum进行统一维护。不同的业务使用不同的错误码。方便在报错时进行分辨。快速定位问题。


多环境配置

SpringBoot多环境配置

对于一个项目来讲基本都4有个环境dev,test,pre,prod,对于SpringBoot项目多建立几个配置文件就可以了。

然后启动的时候可以通过配置spring.profiles.active 来选择启动的环境。

java -jar BasicProject.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@
使用案例
mvn clean package -P prod
mvn clean package -P pre
mvn clean package -P test

打包完可以解压开查看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。

结尾

如果觉得对你有帮助,可以随手点个在看哦,谢谢。

热门内容:
  • 卸载Notepad++!事实已证明,它更牛逼……

  • 聊聊订单系统的设计?

  • 讨论:Service层需要接口吗?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

一个 SpringBoot 项目该包含哪些?相关推荐

  1. 创建一个SpringBoot项目

    Spring的诞生是为了简化JAVA程序的开发的 快速开发Spring而诞生的 SpringBoot为了快速开发Spring而诞生的一个框架 1)什么是SpringBoot?为什么要学它?(重要) S ...

  2. IDEA创建一个springboot项目(三)整合swagge接口测试框架

    我是在上一篇的demo基础上增加的,上一篇地址:springboot项目(二)整合TKMytis框架 一:认识Swagger Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RE ...

  3. eclipse启动一个Springboot项目

    1.准备一个Springboot项目 2.配置好maven 注:本地的maven-repository默认路径是在系统盘的.m文件夹.如果想要修改可参考: eclipse修改maven仓库的位置_本本 ...

  4. 四、创建第一个springboot项目

    简介 spring boot 它的设计目的就是为例简化开发,开启了各种自动装配,你不想写各种配置文件,引入相关的依赖就能迅速搭建起一个web工程.它采用的是建立生产就绪的应用程序观点,优先于配置的惯例 ...

  5. docker 中部署一个springBoot项目

    docker 中部署一个springBoot项目 (1)介绍 springBoot项目 1.项目结构 2.pom.xml [java] view plaincopy <?xml version= ...

  6. maven 不编译jasper文件_第一个SpringBoot项目、核心配置文件properties(yml、yaml)、集成jsp...

    SpringBoot简介及国内关注度 SpringBoot简介: 它用来简化 Spring 应用程序的创建和开发过程,也可以说 Spring Boot 能简化我们之前采用 SpringMVC +Spr ...

  7. 使用 idea 创建第一个 springboot 项目

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 如今springboot越来越火,越来越多的公司选择使用springboot作为项目的开发框架,其设 ...

  8. 我的第一个SpringBoot项目

    创建我的第一个SpringBoot项目. 打开Eclipse右击选择new >> project 进入之后找到SpringBoot点击打开找到Spring Starter Project ...

  9. jsp拿不到回显数据_第一个SpringBoot项目、核心配置文件properties(yml、yaml)、集成jsp...

    SpringBoot简介及国内关注度 SpringBoot简介: 它用来简化 Spring 应用程序的创建和开发过程,也可以说 Spring Boot 能简化我们之前采用 SpringMVC +Spr ...

最新文章

  1. GIT使用总结(二)
  2. 写论文前先交提纲,没准儿提前就被顶刊接收!Nature:论文还没写,就知道是好研究...
  3. 【基础算法-模拟-例题-*校长的问题】-C++
  4. opencv python安装linux_Ubuntu16.04、Python3.6下安装opencv4遇到的问题
  5. 前端学习(2050)vue之电商管理系统电商系统之实现node创建服务器
  6. mac 安装mysql怎么卸载不干净_CleanMyMac卸载不干净怎么办?如何彻底删除Mac上的CleanMyMac?...
  7. linux查看cpu核数_我们常说的CPU核数指的是什么?
  8. APP审核关于3.2.1金融资格的审核回复苹果
  9. Python 机器学习——线性代数和矩阵运算:从matlab迁移到python
  10. 看《乡村爱情》,秒懂区块链!
  11. Mongo 多语言模糊匹配
  12. 微信小程序识别图片并提取文字_微信小程序图片上传(文字识别)
  13. obd 与服务器通讯协议,OBD系统的通讯协议介绍 - 〖汽车维修设备制作〗 - 中威汽车电子技术论坛 - Powered by Discuz!...
  14. 基于java+ssm+mysql的医院管理系统
  15. 统计英文文件中单词出现频率
  16. 程序员的算法趣题Q68: 异性相邻的座位安排(1)
  17. 吐血整理!14个编写Spring MVC控制器的实用小技巧
  18. 装修日记 20050306 选择木地板
  19. fontcreator制作iconfont(包含两个教程)
  20. 2020年云南统招专升本部分院校录取分数线

热门文章

  1. Centos7 安装 telnet 服务
  2. 对于装饰器Decorator的理解
  3. Java核心技术第五章——2.Object类
  4. Python LEGB (Local, Enclosing, Global, Build in) 规则
  5. 【洛谷 1345】 奶牛的电信
  6. 添加MySql数据库超时设置的相关问题
  7. 推荐60+ Flex开发参考网站
  8. 管理输入光标的 WIN32 API 函数
  9. 电子学会青少年编程等级考试Python一级题目解析12
  10. 技术图文:C#语言中的泛型 II