天天看点

爬山的蜗牛旅程:四、springboot整合全局异常(包含404)spring boot全局异常处理(开始正文)

学习springboot的旅程,就像蜗牛爬山,一点点的往上爬,一点点的欣赏旅途的风景

继续上一章的问题,在前后分离或不分离的情况下,要怎么管理和处理全局异常?

首先什么异常,简单说就是系统运行过程中的报错!

系统运行过程中的报错可分为哪些?(个人理解)

  • 由请求触发
  • 非请求触发的(例如定时任务等)–此类异常通常手动捕获或日志处理

由请求触发的异常可分为哪些?(个人理解)

  • 第一种:业务逻辑处理的异常,io流异常/空指针/数组下标越界/类型转换异常等,是在业务层面的异常,是进入Controller层之后的异常
  • 第二种:非业务逻辑处理的异常,404,是进入Controller层之前的异常

请求的类型(个人理解)

  • 非跨域视图请求
  • 非跨域数据请求(ajax)
  • 跨域视图请求
  • 跨域数据请求(ajax)

springboot对于进入Controller层之前的异常:是有springboot自己的视图实现(也就是对于404的异常,springboot有自己的展示视图)–此处要另外处理!

引入Controller功能增强标签(作为知识点补充积累)

  • 先引入4个标签:@ControllerAdvice、@ExceptionHandler、@InitBinder、@ModelAttribute
    • @ControllerAdvice:是SpringMvc引入的新注解,是用来增强Controller的
    • 后三个是配套子标签,分别对应:全局异常处理、全局数据预处理、全局数据绑定
@RestController
@ControllerAdvice  
public class ControllerAdviceTest { 
   /**
    *  全局数据绑定:就是进入所有controller层,且@RequestMapping修饰方法前,
    *   可以通过Model 先绑定全局数据,供@RequestMapping修饰方法调用和使用。
    *   1-把这个map放入Model model
    *   2-同时把Model model作为参数传入所有@RequestMapping注解方法
    */
   @ModelAttribute(name = "md")  //md是放入model的key
   public Map<String, Object> ModelAttribute() {
       HashMap<String, Object> map = new HashMap<>();
       map.put("test", "test");
       return map;
   }
   
   /**
    * Model 使用的例子
    */
   @RequestMapping("/test")
   public String test(Model model) {
       System.out.println(model.get("md"));
   }
   
//----------------------------------------------------------------------------------

   /**
   *  预处理前端传回的参数,比如:两个实体Book,User,都有相同的属性名name
   *  前端传参数:a.name=a&b.name=b
   */
   @InitBinder("b")
   public void b(WebDataBinder binder) {
       binder.setFieldDefaultPrefix("b.");
   }
   @InitBinder("a")
   public void a(WebDataBinder binder) {
       binder.setFieldDefaultPrefix("a.");
   }
   
   @RequestMapping("/test")
   public void test(@ModelAttribute("b") Book book, @ModelAttribute("a") User user) {
   }

//----------------------------------------------------------------------------------

   /**
   *  所有controller层@RequestMapping修饰方法 抛出的异常都会进下面的方法。
   *  但是404不进下面方法,404是进入controller前的异常
   */
   @ResponseBody
   @ExceptionHandler(value = Exception.class)
   public String processUnauthenticatedException(HttpServletRequest request, Exception e) {
       return "viewName"; //返回一个逻辑视图名  
   }
}  
           

spring boot全局异常处理(开始正文)

  • 进入Controller层之后的异常处理–404除外
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理类(除404外)
 */
@ControllerAdvice
public class GlobalExceptionHandler{
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 无论什么错误都跳转页,除非ajax
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response,Exception e){
        Map<String,Object> model = new HashMap<String,Object>();
        model.put("code",500);
        model.put("msg","后台打盹了,程序猿该起床干活啦!!!");

        //发生异常进行日志记录,写入数据库或者其他处理,此处省略
        logger.error("后台发生错误",e);

        /*System.out.println("开始抓异常咯====》》》");
        for(StackTraceElement se:e.getStackTrace()) {
            System.out.println("捕获了异常==》"+se.toString());
        }
        System.out.println("异常都抓完咯====》》》");*/

        //当ajax请求时
        if(isAjax(request,response)){
            response.setHeader("Cache-Control", "no-store");
            response.setCharacterEncoding("UTF-8");
            model.put("success","true");
            PrintWriter pw = null;
            try {
                pw=response.getWriter();
                pw.write(JSONObject.toJSON(model).toString());
                pw.flush();
            } catch (IOException ee) {
                ee.printStackTrace();
            }finally{
                pw.close();
            }

            ModelAndView mv=new ModelAndView();
            mv.clear();
            return mv;
        }

        return new ModelAndView("com/error/404", model);
    }

    /**
     * 判断是否ajax(是否跨域请求)
     * @param request
     * @param response
     * @return
     */
    public boolean isAjax(HttpServletRequest request, HttpServletResponse response) {
        if (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {//非跨域的ajax
            return (true);
        }
        if (request.getHeader("referer") != null && !"".equals(request.getHeader("referer"))){//跨域的ajax
            return (true);
        }
        return (false);
    }
}
           
  • (404/找不到请求/找不到文件js/css)等异常处理
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.error.ErrorController;
@Controller
public class NotFoundException implements ErrorController {
   private final Logger logger = LoggerFactory.getLogger(this.getClass());

   @Override
   public String getErrorPath() {
       return "/error";
   }


   @RequestMapping(value = {"/error"})
   @ResponseBody
   public ModelAndView error(HttpServletRequest request) {
       Map<String,Object> model = new HashMap<String,Object>();
       model.put("code",404);
       model.put("mag","请查看你请求的路径或文件是否存在,或者请求的路径是否正确");
       //System.out.println(request.getRequestURL()+"真的404==》请查看你请求的路径或文件是否存在,或者请求的路径是否正确!!!");
       //发生异常进行日志记录,写入数据库或者其他处理,此处省略
       logger.error("请求异常","请求的文件或者路径真的404!!!");
       return new ModelAndView("com/error/404", model);
   }
}
           

至此springboot整合全局异常就OK了。亲测可用,同嫂无欺!!! 下面开始要干嘛呢??是不是要考虑日志管理呢,昨晚日日夜夜都在挖坑的我,日志的管理是必须的!!

继续阅读