Spring源码中有关 @ControllerAdvice
的注解如下:
Specialization of {@link Component @Component} for classes that declare {@link ExceptionHandler @ExceptionHandler}, {@link InitBinder @InitBinder}, or {@link ModelAttribute @ModelAttribute} methods to be shared across multiple {@code @Controller} classes.
理解:
@ControllerAdvice
是一个特殊的 @Component
,用于标识一个类,这个类中被以下三种注解标识的方法: @ExceptionHandler
, @InitBinder
, @ModelAttribute
,将作用于所有的 @Controller
类的接口上。
那么,这个三个注解分别是什么意思,起到什么作用呢?
Annotation that identifies methods which initialize the {@link org.springframework.web.bind.WebDataBinder} which will be used for populating command and form object arguments of annotated handler methods. Such init-binder methods support all arguments that {@link RequestMapping} supports, except for command/form objects and corresponding validation result objects. Init-binder methods must not have a return value; they are usually declared as {@code void}.
作用:注册属性编辑器,对HTTP请求参数进行处理,再绑定到对应的接口,比如格式化的时间转换等。应用于单个@Controller类的方法上时,仅对该类里的接口有效。与@ControllerAdvice组合使用可全局生效。
示例:
@ControllerAdvice public class ActionAdvice { @InitBinder public void handleException(WebDataBinder binder) { binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); } } 复制代码
作用:统一异常处理,也可以指定要处理的异常类型
示例:
@ControllerAdvice public class ActionAdvice { @ExceptionHandler(Exception.class) @ResponseBody @ResponseStatus(HttpStatus.OK) public Map handleException(Exception ex) { Map<String, Object> map = new HashMap<>(); map.put("code", 400); map.put("msg", ex.toString()); return map; } } 复制代码
作用:绑定数据
示例:
@ControllerAdvice public class ActionAdvice { @ModelAttribute public void handleException(Model model) { model.addAttribute("user", "zfh"); } } 复制代码
在接口中获取前面绑定的参数:
@RestController public class BasicController { @GetMapping(value = "index") public Map index(@ModelAttribute("user") String user) { //... } } 复制代码
完整示例代码:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.format.datetime.DateFormatter; import org.springframework.http.HttpStatus; import org.springframework.ui.Model; import org.springframework.validation.Validator; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; /** * 统一异常处理 * @author zfh * @version 1.0 * @since 2019/1/4 15:23 */ @ControllerAdvice public class ControllerExceptionHandler { private Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class); @InitBinder public void initMyBinder(WebDataBinder binder) { // 添加对日期的统一处理 //binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); // 添加表单验证 //binder.addValidators(); } @ModelAttribute public void addMyAttribute(Model model) { model.addAttribute("user", "zfh"); // 在@RequestMapping的接口中使用@ModelAttribute("name") Object name获取 } @ExceptionHandler(value = Exception.class) @ResponseStatus(HttpStatus.OK) @ResponseBody // 如果使用了@RestControllerAdvice,这里就不需要@ResponseBody了 public Map handler(Exception ex) { logger.error("统一异常处理", ex); Map<String, Object> map = new HashMap<>(); map.put("code", 400); map.put("msg", ex); return map; } } 复制代码
测试接口:
@RestController public class TestAction { @GetMapping(value = "testAdvice") public JsonResult testAdvice(@ModelAttribute("user") String user, Date date) throws Exception { System.out.println("user: " + user); System.out.println("date: " + date); throw new Exception("直接抛出异常"); } } 复制代码
使用 @ControllerAdvice
+ @InitBinder
,可将http请求参数中的时间自动转换成Date类型。
@InitBinder public void initBinder(WebDataBinder binder) { GenericConversionService genericConversionService = (GenericConversionService) binder.getConversionService(); if (genericConversionService != null) { genericConversionService.addConverter(new DateConverter()); } } 复制代码
自定义的时间类型转换器:
import org.springframework.core.convert.converter.Converter; import org.springframework.util.StringUtils; import java.text.SimpleDateFormat; import java.util.Date; /** * 日期转换类 * 将标准日期、标准日期时间、时间戳转换成Date类型 */ public class DateConverter implements Converter<String, Date> { private static final String dateFormat = "yyyy-MM-dd HH:mm:ss"; private static final String shortDateFormat = "yyyy-MM-dd"; private static final String timeStampFormat = "^//d+$"; @Override public Date convert(String value) { if(StringUtils.isEmpty(value)) { return null; } value = value.trim(); try { if (value.contains("-")) { SimpleDateFormat formatter; if (value.contains(":")) { formatter = new SimpleDateFormat(dateFormat); } else { formatter = new SimpleDateFormat(shortDateFormat); } return formatter.parse(value); } else if (value.matches(timeStampFormat)) { Long lDate = new Long(value); return new Date(lDate); } } catch (Exception e) { throw new RuntimeException(String.format("parser %s to Date fail", value)); } throw new RuntimeException(String.format("parser %s to Date fail", value)); } } 复制代码
扩展:
@RestControllerAdvice
= @ControllerAdvice
+ @ResponseBody
参考: