最近在新项目的开发过程中,遇到了个问题,需要将一些异常的业务流程返回给前端,需要提供给前端不同的响应码,前端再在次基础上做提示语言的国际化适配。这些异常流程涉及业务层和控制层的各个地方,如果每个地方都写一些重复代码显得很冗余。
然后查询解决方案时发现了@ControllerAdvice这个注解,可以对业务异常进行统一处理。经过仔细了解后,发现这个注解还有更多的用处,都很实用。
1 ControllerAdvice介绍
@ControllerAdvice一般和三个以下注解一块使用,起到不同的作用,
- @ExceptionHandler: 该注解作用于方法上,,可以捕获到controller中抛出的一些自定义异常,统一进行处理,一般用于进行一些特定的异常处理。
- @InitBinder:该注解作用于方法上,用于将前端请求的特定类型的参数在到达controller之前进行处理,从而达到转换请求参数格式的目的。
- @ModelAttribute:该注解作用于方法和请求参数上,在方法上时设置一个值,可以直接在进入controller后传入该参数。
2 ControllerAdvice应用场景
2.1@ExceptionHandler统一处理业务异常
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 这里就是对各个层返回的异常进行统一捕获处理
@ExceptionHandler(value = BusinessException.class)
public ResponseData<Void> bizException(BusinessException e){
log.error("业务异常记录",e);
return ResponseData.error(e.getCode(),e.getMessage());
}
}
//业务异常处代码示例:
if(CollectionUtil.isNotEmpty(companies)){
// 通过BusinessExceptionEnum枚举对业务异常进行统一管理
throw new BusinessException(BusinessExceptionEnum.ERROR_10003);
}
需要注意的是,如果这里有多个ExceptionHandler,按照异常类的层次体系,越高层的异常,优先级越低。
2.2@InitBinder做日期格式的统一处理
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 将前端传入的字符串时间格式转换为LocalDate时间
@InitBinder
protected void initBinder(WebDataBinder binder) {
//将前端传入的字符串格式时间数据转为LocalDate格式的数据
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
}
});
//将前端传入的字符串格式时间数据转为LocalDateTime格式的数据
binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(LocalDateTime.parse(text, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
}
});
//将前端传入的字符串格式时间数据转为LocalTim格式的数据
binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
}
});
}
}
// controller进行参数绑定
public ResponseData<List<WorkCalendarVo>> listWorkCalendar(@RequestParam LocalDate date){}
2.3 ModelAttribute提前绑定全局user对象
// 这里@ModelAttribute("loginUser")标注的modelAttribute()方法表示会在Controller方法之前将user设置到contoller里的已绑定参数里
@ModelAttribute("loginUser")
public User setLoginUser(HttpServletRequest request) {
return LoginContextUtils.getLoginUser(request);
}
// 使用
@PostMapping("/list")
public ResponseData<IPage<EmployeeVo>> listEmployee(@ModelAttribute("loginUser") User user, @RequestBody EmployeeSearch employeeSearch){
return ResponseData.success(employeeService.listEmployee(user, employeeSearch));
}
3 ControllerAdvice作用原理探究
在探究ControllerAdvice如何生效时,不得不提到springMvc绕不过的DispatcherServlet,这个类是SpringMVC统一的入口,所有的请求都通过它,里面的一些初始化方法如下。
public class DispatcherServlet extends FrameworkServlet {
// ......
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context