Spring Boot 中的几种统一功能处理方式 (共性下沉, 个性提取)
- 拦截器
- 统一数据返回格式
- 统一异常处理
拦截器
Spring MVC 中的一个组件, 用来拦截用户的请求, 在指定方法前后, 根据业务需要执行预先设定的代码
拦截器的使用步骤
- 定义拦截器
- 注册配置拦截器
定义拦截器
实现 HandlerInterceptor 接口, 并重写其所有方法
preHandler() 方法 : 在目标方法执行前执行 , 返回值 true 表示允许继续执行目标方法, 返回值 false 表示中断目标方法的执行
postHandler() 方法 : 目标方法执行后执行
afterCompletion() 方法 : 视图渲染完成后, 执行该方法内容
@Slf4j @Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("LoginInterceptor 目标方法执行前, 执行代码 ..."); return true; // 默认全部放行 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("LoginInterceptor 目标方法执行后, 执行代码 ..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("LoginInterceptor 视图渲染完成后, 执行代码 ..."); } }
注册配置拦截器
实现 WebMvcConfigurer 接口, 并重写 addInterceptors 方法
@Configuration public class WebConfig implements WebMvcConfigurer { // 获取拦截器对象 @Autowired private LoginInterceptor loginInterceptor; private List<String> excludePath = Arrays.asList( "user/login", "/**/**.html", "/css/**", "/js/**", "/img/**" ); @Override public void addInterceptors(InterceptorRegistry registry) { // 注册自定义拦截对象 registry.addInterceptor(loginInterceptor) .addPathPatterns("/**") // 设置拦截器的拦截路径 .excludePathPatterns("/login.html") // 设置拦截器的放行路径 .excludePathPatterns(excludePath); } }
常见拦截路径的设置 :
/* :
一级路径
/** :
任意级路径
/book/* :
/book 下的一级路径
/book/** :
/book 下的任意级路径
添加拦截器之后的执行流程
- 添加拦截器后, 在执行每个方法之前, 都会先进行判定是否在拦截路径 / 放行路径中, 并判定是否执行拦截器内容
- 被拦截住的请求, 会先执行
preHandler()
方法, 该方法返回一个布尔值. 返回 true 代表该方法被放行, 返回 false 表示该方法不会被放行(即不会执行) - 请求执行完毕后, 再执行
postHandler()
方法 - 待视图渲染完成后, 再执行
afterComletion()
方法
统一数据返回格式
统一数据返回格式使用
@ControllerAdvice
和 实现ResponseBodyAdvice
接口的方式实现
@ControllerAdvice :
表示该类为 控制器通知类
ResponseBodyAdvice 接口 :
内含 supports 方法
和 beforeBodyWrite 方法
supports :
判断是否执行 beforeBodyWtire 方法, 返回值为 true 为执行, false 为不执行.
ResponseBodyWrite :
对 response 方法进行具体操作处理统一数据返回格式优点
- 方便前端程序员接收和解析后端数据接口返回的数据
- 方便后端部门的统一规范的标准执行
- 有利于项目统一数据的维护和修改
首先得有一个数据返回格式
@Data public class Result<T> { private int status; private String err = ""; private T data; //成功 public static <T>Result<T> success(T data) { Result<T> result = new Result<T>(); result.setStatus(200); result.setData(data); return result; } // 失败 public static <T>Result<T> fail(T data, String err) { Result<T> result = new Result<T>(); result.setStatus(-1); result.setErr(err); result.setData(data); return result; } public static <T>Result<T> fail(String err) { Result<T> result = new Result<T>(); result.setStatus(-1); result.setErr(err); return result; } // 未登录 public static <T>Result<T> unlogin() { Result<T> result = new Result<T>(); result.setStatus(-2); result.setErr("用户未登录"); return result; } }
统一数据格式 具体执行代码
@ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Autowired private ObjectMapper objectMapper; @Override public boolean supports(MethodParameter returnType, Class converterType) { // 表示对哪些请求执行 统一数据返回格式处理 (这里就简单的全部执行) return true; } @SneakyThrows @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if(body instanceof Result) { return body; } if(body instanceof String) { // 需要对 String 类型的 body 进行序列化处理 (使用 Spring Boot 内置的 Jackson 工具) return objectMapper.writeValueAsString(Result.success(body)); } return Result.success(body); } }
统一异常处理
统一异常处理使用
@ControllerAdvice
和@ExceptionHandler
注解实现
@ControllerAdvice :
表示控制器通知类
@ExceptionHandler :
表示异常处理器
两个注解搭配使用, 表示当出现某个异常的时候, 执行对应的方法事件
@ResponseBody @ControllerAdvice public class ErrorAdvice { @ExceptionHandler public Result handler(Exception e) { return Result.fail(e.getMessage()); } @ExceptionHandler public Result handler(NullPointerException e) { return Result.fail("发生空指针异常: " + e.getMessage()); } @ExceptionHandler public Result handler(ArithmeticException e) { return Result.fail("发生算数异常:" + e.getMessage()); } }