SpringBoot下构建良好的后端开发规范
前言
本文将从最基础的Java SpringBoot环境,编写一个最基础的接口。
比在此基础上逐渐延申,最终形成一套标准的企业级后端接口开发规范。
1.环境搭建
1.1创建Maven工程
1.2引入所需依赖
4.0.0 com.qilixiang backend_api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.3.3.RELEASE 1.8 3.1.1 8 8 org.springframework.boot spring-boot-starter-web org.projectlombok lombok org.springframework.boot spring-boot-starter-test test com.alibaba fastjson 1.2.83 cn.hutool hutool-all 5.7.22 org.aspectj aspectjrt 1.9.1 org.aspectj aspectjweaver 1.8.13 com.baomidou mybatis-plus-boot-starter 3.1.0 mysql mysql-connector-java runtime junit junit test org.apache.commons commons-lang3 3.7 javax.validation validation-api 2.0.1.Final org.hibernate hibernate-validator 6.0.16.Final validation-api javax.validation com.github.xiaoymin knife4j-spring-boot-starter 2.0.1 org.springframework.boot spring-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;}
这样做有以下不好的地方
- 代码冗长,业务不够清晰
- 后续还有很多校验,又得再写一套,复用性不高
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);}
测试
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目录下)
logback debug ${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%n UTF-8 ${LOG_HOME}/%d{yyyy-MM-dd}.%i.log 100MB 15 500MB true
配置文件
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
启动项目,访问 项目地址:端口/doc.html
最终的项目接口
标签:
相关文章
-
无相关信息