@ControllerAdvice
就是 @Controller 的增强版。主要用来处理全局数据,一般搭配 @InitBinder
、@ModelAttribute
、@ExceptionHandler
使用。在@ControllerAdvice注解类的内部使用以上三个注解的方法会应用到所有的@RequestMapping
注解的方法。
注:
@RestControllerAdvice
,即@RestControllerAdvice = @ControllerAdvice + @ResponseBody
下面将分别介绍:
作用:对HTTP请求参数进行预处理,再绑定到对应的接口。这里我们就可以对参数做一些额外的处理,比如时间格式的转换等。(注意:这里对@RequestBody注解的参数无效,需要自行实现)
在Spring MVC的web项目中,相信小伙伴们经常会遇到一些前端给后端传值比较棘手的问题:
比如后端Controller如何接收Date
日期类型的参数,业界比较通用的做法是用时间戳
或者用字符串
或者使用@DateTimeFormat
注解,但是这还需要我们自己做后续的处理转换,或者每一个日期类型的参数都需要加注解处理,用起来麻烦而且不够优雅,这里看我们的@InitBinder是怎么处理的:
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.util.Date;
@ControllerAdvice
public class GlobalHandler {
private static String[] patterns = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"
};
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
Date date = null;
try {
if (text != null) {
date = DateUtils.parseDate(text, patterns);
}
} catch (ParseException e) {
}
setValue(date);
}
});
}
}
这样当前后端再需要传日期类型的参数时,后端可以直接使用Date
进行接收了。
作用:绑定数据
这个注解大家应该很熟悉了,可以在进入接口前绑定一些数据
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.HashMap;
@ControllerAdvice
public class GlobalHandler {
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("msg", "Hello World");
HashMap<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 20);
model.addAttribute("info", map);
}
}
作用:统一异常处理
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalHandler.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public Map<String, Object> handleException(Exception e) {
LOGGER.error(e.getMessage(), e);
HashMap<String, Object> map = new HashMap<>(3);
map.put("code", 500);
map.put("msg", "服务器内部错误");
return map;
}
}
使用时:
这样,对于RequestMapping标记的方法,只要抛出了Exception异常,便会被此方法拦截,从而返回我们封装标准的响应格式或者view,这里我只处理了Exception异常,还可以指定其他自定义的业务异常等等。
当定义了多个异常处理器的时候,比如@ExceptionHandler(Exception.class)
、@ExceptionHandler(ParseException.class)
,如果程序中抛出了异常,那么它将尝试查找为处理异常而注册的最“特定”的异常处理程序。如果没有这样的处理程序,它将尝试检查异常的超类,如果它也没有找到,它会更高级别等等等等,从最具体到一般。