素材巴巴 > 程序开发 >

SpringBoot下构建良好的后端开发规范

程序开发 2023-09-03 11:10:10

前言

本文将从最基础的Java SpringBoot环境,编写一个最基础的接口。

比在此基础上逐渐延申,最终形成一套标准的企业级后端接口开发规范。

1.环境搭建

1.1创建Maven工程

1.2引入所需依赖


 4.0.0com.qilixiangbackend_api1.0-SNAPSHOTorg.springframework.bootspring-boot-starter-parent2.3.3.RELEASE1.83.1.188org.springframework.bootspring-boot-starter-weborg.projectlomboklombokorg.springframework.bootspring-boot-starter-testtestcom.alibabafastjson1.2.83cn.hutoolhutool-all5.7.22org.aspectjaspectjrt1.9.1org.aspectjaspectjweaver1.8.13com.baomidoumybatis-plus-boot-starter3.1.0mysqlmysql-connector-javaruntimejunitjunittestorg.apache.commonscommons-lang33.7javax.validationvalidation-api2.0.1.Finalorg.hibernatehibernate-validator6.0.16.Finalvalidation-apijavax.validationcom.github.xiaoyminknife4j-spring-boot-starter2.0.1org.springframework.bootspring-boot-maven-plugin
 

1.3启动类

/*** @author com.qilixiang* @date 2022/12/26 15:50*/
 @SpringBootApplication
 @Slf4j
 @EnableScheduling
 @EnableAsync
 public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
 }
 

1.4配置文件

server:port: 8024
 spring:application:name: apidatasource:driver-class-name: com.mysql.cj.jdbc.Driver#    需要修改db连接url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=GMT%2B8username: rootpassword: root
 

1.5测试接口

@RestController
 @RequestMapping("user")
 public class UserController {@RequestMapping("/add")public String add() {return "SUCCESS";}}
 

目录结构

启动项目,请求接口测试

localhost:8024/user/add

2.统一返回数据格式

一个项目内的所有接口,必须有统一的风格,统一返回格式。

其中,返回需要包括最主要的三部分:状态码、信息、数据。状态码是其中最重要的,需要明确,且根据场景去分配。不妨可以参考下一些第三方的api,无一例外都是契合这个标准的。

构建接口返回结果类

// 默认code 1=成功、0=失败,这里不建议像这样写死状态码,应该用枚举维护。
 @Data
 public class Result implements Serializable {private int code = 1;private String message;private T data;public Result() {}public Result(int code) {this.code = code;}public Result(int code, String message) {this.code = code;this.message = message;}public Result(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}public static  Result success(T data) {Result result = new Result();result.setData(data);return result;}public static  Result error(int code, String message) {Result result = new Result(code, message);return result;}public static  Result error(String message) {Result result = new Result(0, message);return result;}
 }
 

改造测试接口

    @PostMapping("/add")public Result addUser() {return Result.success("SUCCESS");}
 

改造前

改造后

3.统一异常处理

为什么要配置统一异常处理

在用户体验方面来解释,代码中不可避免会触发一下bug,当bug发生时,给用户的反馈一般有以下两种情况。

对于用户来说,第一种反馈是十分不友好的,在用户的视角就是一堆乱码。第二种,起码能让用户知道,是服务器内部繁忙,就算其实不是。

配置全局异常处理

首先需要新建一个类,在这个类上加上 @ControllerAdvice/@RestControllerAdvice注解 , 这个类就配置成全局处理类了。然后在类中新建一系列方法,在方法上加上 @ExceptionHandler注解 并指定想处理的异常类型,接着在方法内编写对该异常的操作逻辑,就完成了对该异常的全局处理。

@RestControllerAdvice
 @Slf4j
 public class GlobalExceptionHandler {/*** 捕获全局所有异常*/@ExceptionHandler(value = Exception.class)public Result exceptionHandler(Exception e) {log.error("出现未知异常 -> ", e);return Result.error(e.getMessage());}
 }

测试接口

    @RequestMapping("/test")public void test() {int i = 1 / 0;}
 

更多异常捕获

@RestControllerAdvice
 @Slf4j
 public class GlobalExceptionHandler {/*** 捕获全局异常,处理所有不可知的异常*/@ExceptionHandler(value = Exception.class)public Result exceptionHandler(Exception e) {log.error("出现未知异常 -> ", e);return Result.error(e.getMessage());}/*** 捕获空指针*/@ExceptionHandler(value = NullPointerException.class)public Result npeExceptionHandler(NullPointerException e) {log.error("空指针 -> ", e);return Result.error(e.getMessage());}/*** 参数校验异常** @param e* @return*/@ExceptionHandler(MethodArgumentNotValidException.class)public Result ArgumentValidExceptionHandler(MethodArgumentNotValidException e) {// 从异常对象中拿到ObjectError对象ObjectError objectError = e.getBindingResult().getAllErrors().get(0);// 然后提取错误提示信息进行返回return Result.error(objectError.getDefaultMessage());}// 下面添加更多异常捕获
 }
 

4.参数校验

任何、所有、每一个接口必须对参数进行安全校验!!

最简单常见做法,是把将逻辑写在业务代码层里。

    /*** 添加用户** @param user 用户数据* @return*/public boolean add(User user) {if (user == null) {log.info("对象不能为空");return false;}if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getEmail())) {log.info("不能输入空字符串");return false;}if (user.getPassword().length() < 8 || user.getPassword().length() > 12) {log.info("密码长度必须是8-12个字符");return false;}if (!Pattern.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$", user.getEmail())) {log.info("邮箱格式不正确");return false;}// 参数校验通过,这里写上业务逻辑return true;}
 

这样做有以下不好的地方

  1. 代码冗长,业务不够清晰
  2. 后续还有很多校验,又得再写一套,复用性不高

Validator参数校验

在实体类(参数)内部定义校验规则+信息,接收到参数后自动根据事先定义好的规格,校验是否通过。

改造前

@Data
 public class User {private Long id;private String username;private String password;private String email;private Integer age;
 }
 

改造后

@Data
 public class User {@NotNull(message = "用户id不能为空")private Long id;private String username;@NotNull(message = "用户密码不能为空")@Size(min = 8, max = 12, message = "密码长度必须是8-12个字符")private String password;@NotNull(message = "用户邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;private Integer age;
 }
 

在接口需要校验的参数上加上@Valid注解即可。因为已经提前配置了全局异常处理,校验失败的message也能有统一的格式返回。

    @ApiOperation("添加用户")@PostMapping("/add")public Boolean addUser(@RequestBody @Valid User user) {return userService.add(user);}
 

测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8RlixGSE-1672040959439)(SpringBoot后端接口.assets/image-20221226141119219.png)]

5.自定义异常

有时候,有一些异常是我们需要手动抛出的。比如在业务中,有些情况不符合业务逻辑,这时候可以选择手动抛出异常,中止继续操作,或回滚数据,其中,就可以加入我们自定义的异常。

我们现在就来开始写一个自定义异常

/*** 自定义异常,需要继承runtimeException** @author qilixiang* @date 2022/12/26 14:15*/
 @Getter
 public class CommonException extends RuntimeException {private int code;private String msg;public CommonException() {this(0, "接口异常");}public CommonException(String msg) {this(0, msg);}public CommonException(int code, String msg) {super(msg);this.code = code;this.msg = msg;}
 }
 

统一异常处理捕获此自定义异常

    @ExceptionHandler(CommonException.class)public Result APIExceptionHandler(CommonException e) {return Result.error(e.getCode(), e.getMessage());}
 

测试

    @RequestMapping("/test")public void test() {throw new CommonException("自定义异常");}
 

6.统一日志处理

SpringBoot已经对logback做了集成,只需简单配置即可。

logback-spring.xml(resource目录下)


 
 
 
 
 logbackdebug${CONSOLE_LOG_PATTERN}UTF-8${LOG_HOME}/all.log%d{yyyy-MM-dd HH:mm:ss.SSS} -%5level ---[%15.15thread] %-40.40logger{39} : %msg%n%nUTF-8 ${LOG_HOME}/%d{yyyy-MM-dd}.%i.log100MB15500MBtrue
 

配置文件

logging:config: classpath:logback-spring.xml
 

会在项目根目录下生成日志文件夹

7.构建swagger接口文档

配置后开启即可

配置类

@Configuration
 @EnableSwagger2
 public class SwaggerConfig {@Value("${swagger.enable}")private boolean enable;@Beanpublic Docket docket() {return new Docket(DocumentationType.SWAGGER_2).enable(enable).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.qilixiang")).paths(PathSelectors.any()).build();}/*** 文档信息** @return*/public ApiInfo apiInfo() {return new ApiInfoBuilder().title("标题").description("描述").termsOfServiceUrl("qilixiang").contact(new Contact("联系人", "", "邮箱")).version("1.0").build();}
 }
 

application.yml

swagger:enable: true #是否启用swagger
 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6NRdUtld-1672040959440)(SpringBoot后端接口.assets/image-20221226151823144.png)]

启动项目,访问 项目地址:端口/doc.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MVGRgF0F-1672040959441)(SpringBoot后端接口.assets/image-20221226151931838.png)]

最终的项目接口


标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。