@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。
对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:
1.全局异常处理:结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。
2.全局数据预处理:结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。
3.全局数据绑定:结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。
从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。
测试代码:
/**
* @ControllerAdvice 使用场景
*/
@ControllerAdvice(annotations = RestController.class)
public class GlobalControllerHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalControllerHandler.class);
/**
* 把值绑定到Model中,使全局@RequestMapping可以获取到该值
* @param model
*/
@ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("nandao", "hello world");
}
@ModelAttribute(value = "msg")
public String globalModelAttribute() {
System.out.println("hello word。");
return "msg";
}
@ModelAttribute(name = "person")
public Map<String,Object> mydata() {
HashMap<String, Object> map = new HashMap<>();
map.put("age", 99);
map.put("gender", "人");
return map;
}
/**
* 应用到所有被@RequestMapping注解的方法,在其执行之前初始化数据绑定器
*/
@InitBinder
public void globalInitBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
@InitBinder("nan")
public void b(WebDataBinder binder) {
binder.setFieldDefaultPrefix("nan.");
}
@InitBinder("nv")
public void a(WebDataBinder binder) {
binder.setFieldDefaultPrefix("nv.");
}
/**
* 此注解主要是自定义异常功能
* @param e
* @return
* @throws UnsupportedEncodingException
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Object logicExceptionHandler( Exception e) throws UnsupportedEncodingException {
//系统级异常,错误码固定为-1,提示语固定为系统繁忙,请稍后再试
RestResult result = new RestResult("5000", null, ErrorMessage.SYSTEM_EXCEPTION);
//如果是业务逻辑异常,返回具体的错误码与提示信息,即可以自定义多种异常类型
if (e instanceof BadRequestException) {
BadRequestException badRequestException = (BadRequestException) e;
setData(result,badRequestException.getCode(),badRequestException.getErrorMsg(),badRequestException.getInnerCode(),e);
}else if (e instanceof TestException) {
TestException testException = (TestException) e;
setData(result,testException.getCode(),testException.getErrorMsg(),testException.getInnerCode(),e);
}else {
String msg = e.getMessage();
if (!ContMsgExpUtil.isContMsg(msg)){
msg = ErrorMessage.SYSTEM_EXCEPTION +msg;
}
result.setMessage(msg);
logger.error("errorMsg={},innerCode={},exception={}" ,e.getMessage(),5000, e);
}
return JSONObject.toJSON(result);//正式返回给前端信息
}
void setData(RestResult result, int code, String errorMsg, String innerCode, Exception e) throws UnsupportedEncodingException {
result.setMessage(errorMsg);
result.setCode(innerCode);
logger.error("errorMsg={},innerCode={},exception={}" ,errorMsg,innerCode,e);
logger.info("已经爆出异常了啊!!!!!!");
}
}
自定义异常:
/**
* Created by nandao on 2021/12/22.
* 业务逻辑异常类
*/
public class TestException extends RuntimeException {
/**
* 异常信息
*/
private String errorMsg;
/**
* 错误码
*/
private String innerCode;
private int code;
public String getInnerCode() {
return innerCode;
}
public int getCode() {
return code;
}
public String getErrorMsg() {
return errorMsg;
}
public TestException(String errorMsg) {
super(errorMsg);
this.code = 500;
this.innerCode = "5000";
this.errorMsg = errorMsg;
this.errorMsg = "Internal Server Error: "+errorMsg;
}
public TestException(Integer innerCode, String errorMsg) {
super(errorMsg);
this.code = 500;
this.innerCode = "500"+innerCode.toString();
this.errorMsg = errorMsg;
this.errorMsg = "Test Internal Server Error: "+errorMsg;
}
/**
* 抛出逻辑异常的两个静态类,对应上面两种构造方法
* @param errorMsg
* @return
*/
public static TestException le(String errorMsg) {
return new TestException(errorMsg);
}
public static TestException le(Integer innerCode, String errorMsg) {
return new TestException(innerCode,errorMsg);
}
}
此时控制的的代码就会进行功能增强和全局异常处理,业务代码:
/**
* @author wanghuainan
* @date 2021/1/15 14:30
*/
@Slf4j
@RequestMapping("/nandao")
@RestController
public class TestControllerAdviceController {
@RequestMapping(value = "/users", method = RequestMethod.GET)
public void users(Date date) {
System.out.println(date); // Tue May 02 00:00:00 CST 2019
}
@RequestMapping("/indexMap")
public int indexMap(ModelMap modelMap) {
System.out.println(modelMap.get("nandao"));
Object nandao = modelMap.get("nandao");
Object md = modelMap.get("md");
// JSONObject jsonObject = (JSONObject) JSON.toJSON(modelMap);
// JSONObject jsonObject = (JSONObject) JSON.toJSON(nandao);
Map map =(HashMap)md;
String age = ((HashMap) md).get("gender").toString();
log.info(age);
String s = String.valueOf(nandao);
return 1;
}
// 也可以通过@ModelAttribute获取
@RequestMapping("/indexAttribute")
public Object indexAttribute(@ModelAttribute("words") String words) {
System.out.println(words);
return words;
}
//通过get方法获取数据
@GetMapping("/hello")
public String hello(Model model) {
throw TestException.le(333,"参数为空");
/* Map<String, Object> map = model.asMap();
System.out.println(map);
int i = 1 / 0;*/
// return "hello controller advice";
}
@PostMapping("/book")
public void book(@ModelAttribute("b") Book book) {
System.out.println(book);
throw TestException.le("参数为空");
// System.out.println(author);
}
}
多测试几次,总结心得,就会在实战中使用的游刃有余,此篇文章和上一篇文章是一定关系大家可以参考一下相辅相成参考。