spring注解@ControllerAdvice的原理分析和使用

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

}

多测试几次,总结心得,就会在实战中使用的游刃有余,此篇文章和上一篇文章是一定关系大家可以参考一下相辅相成参考

上一篇:acm算法之二分查找


下一篇:计算机等级考试《二级C语言》真题及答案