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());
}
}