GVKun编程网logo

Spring Boot全局异常处理(spring boot全局异常处理器)

16

在这篇文章中,我们将带领您了解SpringBoot全局异常处理的全貌,包括springboot全局异常处理器的相关情况。同时,我们还将为您介绍有关java之SpringBoot全局异常处理实例详解、S

在这篇文章中,我们将带领您了解Spring Boot全局异常处理的全貌,包括spring boot全局异常处理器的相关情况。同时,我们还将为您介绍有关java之Spring Boot全局异常处理实例详解、SpingBoot全局异常处理、Spring Boot (8) 全局异常处理、spring boot --> 全局异常处理的知识,以帮助您更好地理解这个主题。

本文目录一览:

Spring Boot全局异常处理(spring boot全局异常处理器)

Spring Boot全局异常处理(spring boot全局异常处理器)

Spring Boot默认的异常处理机制

    默认情况下,Spring Boot为两种情况提供了不同的响应方式:

    一种是浏览器客户端请求一个不存在的页面或服务端处理发生异常时,一般情况下浏览器默认发送的请求头中Accept: text/html,所以Spring Boot默认会响应一个html文档内容,称作“Whitelabel Error Page”。

    另一种是使用Postman等调试工具发送请求一个不存在的url或服务端处理发生异常时,Spring Boot会返回类似如下的Json格式字符串信息

{
    "timestamp": "2018-05-12T06:11:45.209+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/index.html"
} 

 

 

 

 

默认异常处理 BasicErrorController

    Spring Boot 默认提供了程序出错的映射路径:/error

    这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。也就是:发生错误时,会交由一个叫做 BasicErrorController 的 controller 处理。
    相关BasicErrorController中代码如下:

package org.springframework.boot.autoconfigure.web.servlet.error;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
//会获取当前的错误页面路径
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
    private final ErrorProperties errorProperties;

    //构造器
    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties, Collections.emptyList());
    }

    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }

    //得到错误处理页面路径
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }

    //返回页面形式结果
    @RequestMapping(produces = {"text/html"})
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        //获取已配置的错误页面
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        //如果没有配置,则返回默认的modelAndView,否则返回已配置的错误页面
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    //返回JSON格式的结果
    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = this.getStatus(request);
        return new ResponseEntity(body, status);
    }

    protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        IncludeStacktrace include = this.getErrorProperties().getIncludeStacktrace();
        if (include == IncludeStacktrace.ALWAYS) {
            return true;
        } else {
            return include == IncludeStacktrace.ON_TRACE_PARAM ? this.getTraceParameter(request) : false;
        }
    }

    //得到错误处理配置
    protected ErrorProperties getErrorProperties() {
        return this.errorProperties;
    }
}

    其中的 ErrorProperties :

package org.springframework.boot.autoconfigure.web;

import org.springframework.beans.factory.annotation.Value;

public class ErrorProperties {
    @Value("${error.path:/error}")
    private String path = "/error";

    private boolean includeException;

    private ErrorProperties.IncludeStacktrace includeStacktrace;

    private ErrorProperties.Whitelabel whitelabel;

    public ErrorProperties() {
        this.includeStacktrace = ErrorProperties.IncludeStacktrace.NEVER;
        this.whitelabel = new ErrorProperties.Whitelabel();
    }

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public boolean isIncludeException() {
        return this.includeException;
    }

    public void setIncludeException(boolean includeException) {
        this.includeException = includeException;
    }

    public ErrorProperties.IncludeStacktrace getIncludeStacktrace() {
        return this.includeStacktrace;
    }

    public void setIncludeStacktrace(ErrorProperties.IncludeStacktrace includeStacktrace) {
        this.includeStacktrace = includeStacktrace;
    }

    public ErrorProperties.Whitelabel getWhitelabel() {
        return this.whitelabel;
    }

    public void setWhitelabel(ErrorProperties.Whitelabel whitelabel) {
        this.whitelabel = whitelabel;
    }

    public static class Whitelabel {
        private boolean enabled = true;

        public Whitelabel() {
        }

        public boolean isEnabled() {
            return this.enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }

    public static enum IncludeStacktrace {
        NEVER,
        ALWAYS,
        ON_TRACE_PARAM;

        private IncludeStacktrace() {
        }
    }
}

    可以发现,默认异常处理路径为:/error。默认错误处理页为:error.html

 

修改默认异常处理路径

    在application.properties 配置文件中:

 

自定义错误页面

    覆盖默认的错误页面

    方式1(通用错误页面)、直接在/resources/templates创建error.html ,就可以覆盖默认的Whitelabel Error Page的错误页面。

    此处用的是 thymeleaf 模板,对应的error.html代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
动态error错误页面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>

    这样运行的时候,请求一个不存在的页面或服务端处理发生异常时,展示的自定义错误界面如下:

 

    方式2(具体的某一种错误页面)、根据不同的状态码返回不同的视图页面,也就是对应的404,500等页面。

    静态页面:

        如果只是静态HTML页面,不带错误信息的,在resources/public/下面创建error目录,在error目录下面创建对应的状态码html即可 ,例如,要将404映射到静态HTML文件,您的文件夹结构如下所示:

    静态404.html简单页面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    静态404错误页面
</body>
</html>

    这样访问一个错误路径的时候,就会显示404错误页面:

    如果/resources/templates存在error.html,则状态码错误页面将覆盖error.html,因为具体状态码错误页面优先级比较高。

 

    动态页面:  

        如果是动态模板页面,可以带上错误信息,在resources/templates/下面创建error目录,在error目录下面命名即可:

    这里模拟下500错误,控制层代码,模拟一个除0的错误:

@Controller 
public class WebController extends  AbstractController{ 

    @ResponseBody 
    @RequestMapping(value="/test") 
    public String error(){ 
        int i=5/0; 
        return "ex"; 
    } 
} 

    500.html代码:

<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Title</title> 
</head> 
<body> 
    动态500错误页面 
    <p th:text="${error}"></p> 
    <p th:text="${status}"></p> 
    <p th:text="${message}"></p> 
</body> 
</html> 

    这时访问 http://localhost:8080/test 即可看到如下错误,说明确实映射到了500.html

    如果同时存在静态页面500.html和动态模板的500.html,则后者覆盖前者。即templates/error/这个的优先级比resources/public/error高。

 

    总结:

  • error.html会覆盖默认的 whitelabel Error Page 错误提示
  • 静态错误页面优先级别比error.html高
  • 动态模板错误页面优先级比静态错误页面高

 

 

 

自定义异常处理

    方法1:自定义实现异常处理controller

    1.实现ErrorController:

package com.core.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @Description: 异常映射controller
 */
@Controller
//读取配置文件中的值
@RequestMapping({"${server.error.path:/error}"})
@EnableConfigurationProperties({ServerProperties.class})
public class ExceptionController implements ErrorController {

    private ErrorAttributes errorAttributes;

    @Autowired
    private ServerProperties serverProperties;

    @Value("${server.error.path:/error}")
    private String errorPath;

    /**
     * 初始化ExceptionController
     *
     * @param errorAttributes
     */
    @Autowired
    public ExceptionController(ErrorAttributes errorAttributes) {
        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
        this.errorAttributes = errorAttributes;
    }

    /**
     * 定义404的ModelAndView
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = "text/html", value = "404")
    public ModelAndView errorHtml404(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        return new ModelAndView("/error/404", model);
    }

    /**
     * 定义404的JSON数据
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "404")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error404(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }

    /**
     * 定义500的ModelAndView
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = "text/html", value = "500")
    public ModelAndView errorHtml500(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        return new ModelAndView("error/500", model);
    }


    /**
     * 定义500的错误JSON信息
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "500")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }


    /**
     * Determine if the stacktrace attribute should be included.
     *
     * @param request  the source request
     * @param produces the media type produced (or {@code MediaType.ALL})
     * @return if the stacktrace attribute should be included
     */
    protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        ErrorProperties.IncludeStacktrace include = this.serverProperties.getError().getIncludeStacktrace();
        if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
            return true;
        }
        if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
            return getTraceParameter(request);
        }
        return false;
    }


    /**
     * 获取错误的信息
     *
     * @param request
     * @param includeStackTrace
     * @return
     */
    private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
        WebRequest webRequest = new ServletWebRequest(request);
        return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
    }

    /**
     * 是否包含trace
     *
     * @param request
     * @return
     */
    private boolean getTraceParameter(HttpServletRequest request) {
        String parameter = request.getParameter("trace");
        if (parameter == null) {
            return false;
        }
        return !"false".equals(parameter.toLowerCase());
    }

    /**
     * 获取错误编码
     *
     * @param request
     * @return
     */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        try {
            return HttpStatus.valueOf(statusCode);
        } catch (Exception ex) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }

    @Override
    public String getErrorPath() {
        return this.errorPath;
    }
}

    实现了自定义的404、500错误的映射解析,分别对浏览器请求以及json请求做了回应。

    修改内嵌容器配置,设置错误页面映射:

package com.core.config;

@Configuration
public class MyWebMvcConfig{
    //读取配置文件中的值
    @Value("${server.error.path:/error}")
    private String errorPath;

    @Bean
    //修改内嵌容器的相关配置
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
        return factory -> {//使用lamda表达式
            //定义错误页面映射
            Set<ErrorPage> errorPageSet=new HashSet<ErrorPage>();
            errorPageSet.add(new ErrorPage(HttpStatus.NOT_FOUND,errorPath+"/404"));//404
            errorPageSet.add(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,errorPath+"/500"));//500
            // 可继续添加其他的错误页面映射
            factory.setErrorPages(errorPageSet);
        };
    }
}

    访问请求http://localhost:8080//web/myBlog/index1 ,结果如下:

 

    上述实现,可以简单地实现:

         浏览器访问也返回json字符串(返回text/html)

import java.util.HashMap;
import java.util.Map; 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 
import com.alibaba.fastjson.JSONObject;
import com.xuecheng.framework.model.response.ErrorCode;
import com.xuecheng.framework.model.response.ResultCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
 
@Controller
public class InterfaceErrorController implements ErrorController {
    private static final String ERROR_PATH="/error";
    private ErrorAttributes errorAttributes;
 
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
    @Autowired
    public InterfaceErrorController(ErrorAttributes errorAttributes) {
        this.errorAttributes=errorAttributes;
    }
 
    /**
     * web页面错误处理
     */
    @RequestMapping(value=ERROR_PATH,produces="text/html")
    @ResponseBody
    public String errorPageHandler(HttpServletRequest request,HttpServletResponse response) {
        ServletWebRequest requestAttributes =  new ServletWebRequest(request);
        Map<String, Object> attr = this.errorAttributes.getErrorAttributes(requestAttributes, false);
        JSONObject jsonObject = new JSONObject();
        ErrorCode errorCode = new ErrorCode(false, (int) attr.get("status"), (String) attr.get("message"));
        return JSONObject.toJSONString(errorCode);
    }
 
    /**
     * 除web页面外的错误处理,比如json/xml等
     */
    @RequestMapping(value=ERROR_PATH)
    @ResponseBody
    public ResultCode errorApiHander(HttpServletRequest request) {
        ServletWebRequest requestAttributes = new ServletWebRequest(request);
        Map<String, Object> attr=this.errorAttributes.getErrorAttributes(requestAttributes, false);
        return new ErrorCode(false, (int)attr.get("status"), (String) attr.get("message"));
    }
 
}

    返回自定义的ErrorCode:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
 
@ToString
@Data
@AllArgsConstructor
public class ErrorCode implements ResultCode{
 
    private boolean success;
 
    private int code;
 
    private String message;
 
 
    @Override
    public boolean success() {
        return false;
    }
 
    @Override
    public int code() {
        return 0;
    }
 
    @Override
    public String message() {
        return null;
    }
}

     ResultCode 接口如下:

public interface ResultCode {
    //操作是否成功,true为成功,false操作失败
    boolean success();
    //操作代码
    int code();
    //提示信息
    String message();
}

 

 

    方法2:实现错误处理器

    1.实现 WebMvcConfigurer,增加 自定义的错误处理 HandlerExceptionResolver :

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {

    /**
     * 异常处理器
     *
     * @param exceptionResolvers
     */
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        //可添加多个HandlerExceptionResolver
        //第一个
        exceptionResolvers.add(0, new CustomExceptionResolver());
        //第二个
        exceptionResolvers.add(1, new WebExceptionResolver());
        //无须处理的异常集合,直接返回null,交由其他的异常处理类处理
    }
}

    2.自定义HandlerExceptionResolver 有两种实现:

        实现 HandlerExceptionResolver 接口,(此处 IO错误将返回到error/ioException,SQL错误将返回到error/sqlException):

package com.core.resolver;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @Description:
 * 自定义异常处理
 */
public class CustomExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        // 判断是否已进行了异常日志的记录
        String logging=(String)httpServletRequest.getAttribute("logging");
        if(logging==null||logging==""){
            Logger logger = LogManager.getLogger();
            logger.error(e.getMessage(), e);
            httpServletRequest.setAttribute("logging","true");
        }
        if(e instanceof IOException){//IO错误
            return new ModelAndView("error/ioException","ex",e);
        }else if(e instanceof SQLException){//SQL错误
            return new ModelAndView("error/sqlException","ex",e);
        }else if (e instanceof org.apache.shiro.authz.UnauthorizedException){//shiro 角色、权限验证错误
            ModelAndView mav = new ModelAndView("blank");
            mav.addObject("msg", "您没有足够的角色/权限进行操作!");
            mav.addObject("gotoPage", "login.jsp");
            return mav;
        }else if (e instanceof org.apache.shiro.authz.UnauthenticatedException){//shiro 登陆验证错误
            ModelAndView mav = new ModelAndView("blank");
            mav.addObject("msg", "您需要登陆才能继续操作!");
            mav.addObject("gotoPage", "login.jsp");
            return mav;
        }
        return null;
    }
}

        重写默认异常处理类 SimpleMappingExceptionResolver,(此处对没有进行异常日志记录的异常进行记录) :

package com.core.resolver;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @Description:
 * 重写默认异常处理类
 */
public class WebExceptionResolver extends SimpleMappingExceptionResolver {

    public WebExceptionResolver(){
        super.setExcludedExceptions(IOException.class,SQLException.class);
        super.setDefaultErrorView("error/error");//所有的异常定义默认的异常处理页面
        super.setExceptionAttribute("ex");//异常处理页面用来获取异常信息的变量名
        super.setDefaultStatusCode(500);//默认返回状态码
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 判断是否已进行了异常日志的记录
        String logging=(String)request.getAttribute("logging");
        if(logging==null||logging==""){
            Logger logger = LogManager.getLogger();
            logger.error(ex.getMessage(), ex);
            request.setAttribute("logging","true");
        }
        return super.doResolveException(request, response, handler, ex);
    }
}

    这种配置方法:在错误发生,BasicErrorController 将获取到其配置的错误页面。针对请求过程中,出现的异常均能处理。

    不足之处,对于渲染视图出现的错误,将无法进行处理:

    例如:

    

    

 


    方法2:局部异常处理 @Controller + @ExceptionHandler

    @ExceptionHandler注解到类的方法上,当此注解里定义的异常抛出时,此方法会被执行。

  • 如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。
  • 如果@ExceptionHandler所在的类带有@ControllerAdvice注解,则此方法会作用在所有的controller。

    举个简单例子,这里我们对除0异常用@ExceptionHandler来捕捉。

@Controller
public class WebController extends AbstractController{ 

	@RequestMapping(value="/ex") 
	@ResponseBody 
	public String error(){ 
		int i=5/0; 
		return "ex"; 
	} 

	//局部异常处理 
	@ExceptionHandler(Exception.class) 
	@ResponseBody 
	public String exHandler(Exception e){ 
		// 判断发生异常的类型是除0异常则做出响应 
		if(e instanceof ArithmeticException){ 
			return "发生了除0异常"; 
		} 
		// 未知的异常做出响应 
		return "发生了未知异常"; 
	}
} 

 

    方法3:异常处理 @ControllerAdvice + @ExceptionHandler

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。

    简单的说,进入Controller层的错误才会由@ControllerAdvice处理,拦截器抛出的错误以及访问错误地址的情况@ControllerAdvice处理不了,将由SpringBoot默认的异常处理机制处理。

    我们实际开发中,如果是要实现RESTful API,那么默认的JSON错误信息就不是我们想要的,这时候就需要统一一下JSON格式,所以需要封装一下。

/**
* 返回数据
*/
public class AjaxObject extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public AjaxObject() {
		put("code", 0);
	}

	public static AjaxObject error() {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
	}

	public static AjaxObject error(String msg) {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
	}

	public static AjaxObject error(int code, String msg) {
		AjaxObject r = new AjaxObject();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static AjaxObject ok(String msg) {
		AjaxObject r = new AjaxObject();
		r.put("msg", msg);
		return r;
	}

	public static AjaxObject ok(Map<String, Object> map) {
		AjaxObject r = new AjaxObject();
		r.putAll(map);
		return r;
	}

	public static AjaxObject ok() {
		return new AjaxObject();
	}

	public AjaxObject put(String key, Object value) {
		super.put(key, value);
		return this;
	}

	public AjaxObject data(Object value) {
		super.put("data", value);
		return this;
	}

	public static AjaxObject apiError(String msg) {
		return error(1, msg);
	}
}

    正确默认code返回0,data里面可以是集合,也可以是对象,正确情况返回的就是:

{
    code:0,
    msg:“获取列表成功”,
    data:{ 
        queryList :[]
    }
}

    如果是异常情况,返回的json则是:

{
    code:500,
    msg:“未知异常,请联系管理员”
}

    然后创建一个自定义的异常类:

public class BusinessException extends RuntimeException implements Serializable {

    private static final long serialVersionUID = 1L;
    private String msg;
    private int code = 500;
    
    public BusinessException(String msg) {
        super(msg);
        this.msg = msg;
    }
    
    public BusinessException(String msg, Throwable e) {
        super(msg, e);
        this.msg = msg;
    }
    
    public BusinessException(int code,String msg) {
        super(msg);
        this.msg = msg;
        this.code = code;
    }
    
    public BusinessException(String msg, int code, Throwable e) {
        super(msg, e);
        this.msg = msg;
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

    Controller中添加一个json映射,用来处理这个异常

@Controller
public class WebController{

    @RequestMapping("/json")
    public void json(ModelMap modelMap) {
        System.out.println(modelMap.get("author"));
        int i=5/0;
    }
}

    最后创建异常处理类:

/**
 * 异常处理器
 */
@RestControllerAdvice
public class BusinessExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
     * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
     * @param binder
     */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		System.out.println("请求有参数才进来");
	}

	/**
     * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
     * @param model
     */
	@ModelAttribute
	public void addAttributes(Model model) {
		model.addAttribute("author", "嘟嘟MD");
	}

	@ExceptionHandler(Exception.class)
	public Object handleException(Exception e,HttpServletRequest req){
		AjaxObject r = new AjaxObject();
		//业务异常
		if(e instanceof BusinessException){
			r.put("code", ((BusinessException) e).getCode());
			r.put("msg", ((BusinessException) e).getMsg());
		}else{//系统异常
			r.put("code","500");
			r.put("msg","未知异常,请联系管理员");
		}

		//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
		String contentTypeHeader = req.getHeader("Content-Type");
		String acceptHeader = req.getHeader("Accept");
		String xRequestedWith = req.getHeader("X-Requested-With");
		if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
			|| (acceptHeader != null && acceptHeader.contains("application/json"))
			|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
			return r;
		} else {
			ModelAndView modelAndView = new ModelAndView();
			modelAndView.addObject("msg", e.getMessage());
			modelAndView.addObject("url", req.getRequestURL());
			modelAndView.addObject("stackTrace", e.getStackTrace());
			modelAndView.setViewName("error");
			return modelAndView;
		}
	}
}

    @ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。

    其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,此处配置拦截Exception。

    error.html:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">
<head>
    <title>Spring Boot管理后台</title>
    <script type="text/javascript">
    </script>
</head>
<body>
<div layout:fragment="content" th:remove="tag">
    <div  id="navbar">
        <h1>系统异常统一处理</h1>
        <h3 th:text="''错误信息:''+${msg}"></h3>
        <h3 th:text="''请求地址:''+${url}"></h3>

        <h2>Debug</h2>
        <a th:href="@{''https://www.google.com/webhp?hl=zh-CN#safe=strict&hl=zh-CN&q=''+${msg}}"target="_blank" id="Google">Google</a>
        <a th:href="@{''https://www.baidu.com/s?wd=''+${msg}}"target="_blank" id="Baidu">Baidu</a>
        <a th:href="@{''http://stackoverflow.com/search?q=''+${msg}}"target="_blank" id="StackOverFlow">StackOverFlow</a>
        <h2>异常堆栈跟踪日志StackTrace</h2>
        <div th:each="line:${stackTrace}">
            <div th:text="${line}"></div>
        </div>
    </div>
</div>
<div layout:fragment="js" th:remove="tag">
</div>
</body>
</html>

    访问http://localhost:8080/json的时候,因为是浏览器发起的,返回的是error界面:

    如果是ajax请求,返回的就是错误:

{ "msg":"未知异常,请联系管理员", "code":500 }

 

java之Spring Boot全局异常处理实例详解

java之Spring Boot全局异常处理实例详解

这篇文章主要为大家详细介绍了spring boot全局异常处理的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文为大家分享了Spring Boot全局异常处理,供大家参考,具体内容如下

1、后台处理异常

a、引入thymeleaf依赖

<!-- thymeleaf模板插件 -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
登录后复制

b、在application.properties文件中设置属性

立即学习“Java免费学习笔记(深入)”;

#关闭thymeleaf模板的缓存
spring.thymeleaf.cache=false
登录后复制

c、编写后台处理Handler  

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

  //设置此handler处理所有异常
 @ExceptionHandler(value=Exception.class)
 public void defaultErrorHandler(){
 System.out.println("-------------default error");
 }
}
登录后复制

d、后台异常打印

-------------default error
2017-06-16 14:54:05.314 WARN 6892 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.dao.IncorrectResultSizeDataAccessException: result returns more than one elements; nested exception is javax.persistence.NonUniqueResultException: result returns more than one elements

2、页面处理异常

a、编写html模板页面

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" 
  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
 <h1 th:inlines="text">异常出现啦</h1>
 ${messages}
</body>
</html>
登录后复制

b、修改Handler

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

 @ExceptionHandler(value=Exception.class)
 @ResponseBody
 public String defaultErrorHandler(){
  System.out.println("-------------default error");
  return "系统错误,请联系管理员";
 }
}
登录后复制

c、页面访问结果

以上就是java之Spring Boot全局异常处理实例详解的详细内容,更多请关注php中文网其它相关文章!

SpingBoot全局异常处理

SpingBoot全局异常处理

SpingBoot全局异常处理

相关代码: github OSCchina

@controllerAdvice()注解

  • 1.@ControllerAdvice()注解可以定义一个统一的异常处理类,我们可以定义多个统一异常处理类, 但这里我们需要注意一点,默认的@ControllerAdvice()会处理所有的controller层抛出的异常, 如果我们需要对不同的包进行不同的异常处理,比如pc端我们要返回一个jsp页面,app端我们需要返回 一个json,这时候我们可以通过配置如:@ControllerAdvice("com.lc.xxx")或者 @ControllerAdvice(basePackages="com.lc1.xxx,com.lc2.xxx")来对不同包的异常进行处理.

@ExceptionHandler()注解

  • 1.@ExceptionHandler()注解用在方法上,用来处理具体的某一中或某一类异常,比如@ExceptionHandler(Exception.class) 将会处理所有的异常,如果魔门有一个自定的异常MyException继承自Exception,同时配置了@ExceptionHandler(Exception.class) 和@ExceptionHandler(MyException.class),则会根据抛出的具体异常来判断选用哪个处理器,比如,我们抛出了MyException,则会优先使用 MyException的异常处理器.

异常处理器的覆盖

如果我们配置了多个@controllerAdvice(),如果有一个没有指定具体的包名,那么他会覆盖其它所有指定了包名的异常处理类,导致其它失效.

Spring Boot (8) 全局异常处理

Spring Boot (8) 全局异常处理

服务层和dao层的最终异常会交给controller处理,控制层的异常则会记录日志系统。

 

新建一个类用来处理异常,名字随便GlobalDefaultExceptionHandler.java,加上@ControllerAdvice注解

package com.david.utils;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

//捕获controller层抛出的异常,如果需要返回Json格式要添加@ResponseBody @RestControllerAdvice相当于(@ControllerAdvice和@ResponseBody)
@ControllerAdvice
public class GlobalDefaultExceptionHandler {


    //拦截所有Exception
    @ExceptionHandler(Exception.class)
    @ResponseBody //如果返回String 需要加ResponseBody 如果返回View 返回值就是ModelAndView
    public String defaultExceptionHandler(HttpServletRequest request,Exception  ex){

        //日志处理

        return "500 系统错误";
    }

}

 

spring boot --> 全局异常处理

spring boot --> 全局异常处理

package com.jscz;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
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.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.xml.bind.ValidationException;
import java.util.Set;

/**
 * 异常处理类
 */
@ControllerAdvice
@ResponseBody
public class GlobalExceptionController {
    private static Logger logger = LoggerFactory.getLogger(GlobalExceptionController.class);
 
 
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public String handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        logger.error("handleMissingServletRequestParameterException缺少请求参数", e);
        return "handleMissingServletRequestParameterException缺少请求参数";
    }
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public String handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        logger.error("handleHttpMessageNotReadableException参数解析失败", e);
        return "handleHttpMessageNotReadableException参数解析失败";
    }
 
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.error("handleMethodArgumentNotValidException参数验证失败", e);
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        return "handleMethodArgumentNotValidException参数验证失败="+message;
    }
    
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IllegalArgumentException.class)
    public String IllegalArgumentException(MethodArgumentNotValidException e) {
        logger.error("IllegalArgumentException------>>>>>>>>>>>>", e);
        return "IllegalArgumentException------>>>>>>>>>>>>错误";
    }
 
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public String handleBindException(BindException e) {
        logger.error("handleBindException参数绑定失败", e);
        BindingResult result = e.getBindingResult();
        FieldError error = result.getFieldError();
        String field = error.getField();
        String code = error.getDefaultMessage();
        String message = String.format("%s:%s", field, code);
        return "handleBindException参数绑定失败="+message;
    }
 
 
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public String handleServiceException(ConstraintViolationException e) {
        logger.error("handleServiceException参数验证失败", e);
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        ConstraintViolation<?> violation = violations.iterator().next();
        String message = violation.getMessage();
        return "handleServiceException参数验证失败" + message;
    }
 
    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ValidationException.class)
    public String handleValidationException(ValidationException e) {
        logger.error("handleValidationException参数验证失败", e);
        return "handleValidationException参数验证失败";
    }
 
    /**
     * 404 - Not Found
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public String noHandlerFoundException(NoHandlerFoundException e) {
        logger.error("noHandlerFoundException    Not Found", e);
        return "noHandlerFoundException    Not Found="+e;
    }
 
 
    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public String handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        logger.error("handleHttpRequestMethodNotSupportedException    不支持当前请求方法", e);
        return "handleHttpRequestMethodNotSupportedException   request_method_not_supported";
    }
 
    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public String handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        logger.error("handleHttpMediaTypeNotSupportedException不支持当前媒体类型", e);
        return "handleHttpMediaTypeNotSupportedException   content_type_not_supported";
    }
    /**
     * 业务层需要自己声明异常的情况
     */
//    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//    @ExceptionHandler(ServiceException.class)
//    public String handleServiceException(ServiceException e) {
//        logger.error("业务逻辑异常", e);
//        return "业务逻辑异常:" + e.getMessage();
//    }
    /**
     * 操作数据或库出现异常
     */
//    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//    @ExceptionHandler(DataDoException.class)
//    public String handleException(DataDoException e) {
//        logger.error("操作数据库出现异常:", e);
//        return "操作数据库出现异常:字段重复、有外键关联等";
//    }
 
    /**
     * 500 - Internal Server Error
     */
//   @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//    @ExceptionHandler(Exception.class)
//    public String handleException(Exception e) {
//        logger.error("通用异常", e);
//        return "500通用异常:" + e.getMessage();
//    }
 
    /**
     * 获取其它异常。包括500
     * @param e
     * @return
     * @throws Exception
     */
   @ExceptionHandler(value = Exception.class)
    public String defaultErrorHandler(Exception e){
        logger.error("defaultErrorHandler   Exception", e);
 
        return "defaultErrorHandler  其它异常:" + e.getMessage();
 
    }
 
 
 
}
 

今天关于Spring Boot全局异常处理spring boot全局异常处理器的分享就到这里,希望大家有所收获,若想了解更多关于java之Spring Boot全局异常处理实例详解、SpingBoot全局异常处理、Spring Boot (8) 全局异常处理、spring boot --> 全局异常处理等相关知识,可以在本站进行查询。

本文标签: