ErrorController、RestControllerAdvice、ResponseEntityExceptionHandler、HandlerInterceptor
在java web中,原生listener
,filter
, servlet
执行顺序为:
Listener------>Filter----->Servlet
而在SpringMVC中,实质就是一个DispatchServlet
而在Servlet中,则是调用了HandlerInterceptor
的各个方法,和最后ExceptionHandler
处理,调用顺序会变成:
Listener------>Filter----->DispatchServlet ---->HandlerInterceptor.preHandle---->handle---->HandlerInterceptor. postHandle------>ExceptionHandler
所以,在调用过程中,HandlerInterceptor是在Filter调用之后,而ExceptionHandler异常处理也是在Filter之后,同理可得,Filter中的异常是不能被ExceptionHandler处理的。
Spring @ControllerAdvice vs ErrorController
An implementation of the ErrorController
is used to provide a custom whitelabel error page.
A class annotated with @ControllerAdvise
is used to add a global exception handling logic for the whole application. Thus, more than one controller in your application.
错误访问
import com.Utils.ReturnCode;
import com.Utils.ReturnVO;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 在项目中我们遇到404找不到的错误、或者500服务器错误都需要配置
* 相应的页面给用户一个友好的提示,而在Spring Boot中我们需要如何设置。
* 我们需要实现ErrorController接口,重写handleError方法。
* 处理请求要放到启动类下面
*/
@RestController
public class ExceptionHandler implements ErrorController {
@Override
public String getErrorPath() {
/**
* 设置当错误时返回的页面
*/
return null;
// return "/404";
}
@RequestMapping(value = "/error")
public ReturnVO handleError(HttpServletRequest request){
//获取statusCode:401,404,500
// Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code")
// if(statusCode == 401){
// return "/401";
// }else if(statusCode == 404){
// return "/404";
// }else if(statusCode == 403){
// return "/403";
// }else{
// return "/500";
// }
return new ReturnVO(ReturnCode.URL_ERROR);
}
}
控件处理异常捕获
import com.Utils.ReadPropertiesUtil;
import com.Utils.ReturnCode;
import com.Utils.ReturnVO;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Properties;
/**
* 用于全局返回json,如需返回ModelAndView请使用ControllerAdvice
* 继承了ResponseEntityExceptionHandler,对于一些类似于请求方式异常的异常进行捕获
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static Properties properties = ReadPropertiesUtil.getProperties(null);
/**
* 异常捕获
* @param e 捕获的异常
* @return 封装的返回对象
**/
@ExceptionHandler(value = Exception.class)
public String handlerException(Exception e) {
ReturnVO returnVO = new ReturnVO();
String errorName = e.getClass().getName();
errorName = errorName.substring(errorName.lastIndexOf(".") + 1);
//如果没有定义异常,而是直接抛出一个运行时异常,需要进入以下分支
if (e.getClass() == RuntimeException.class) {
returnVO.setMsg(properties.getProperty(ReturnCode.valueOf("EXCEPTION").msg()) +": "+ e.getMessage());
returnVO.setCode(properties.getProperty(ReturnCode.valueOf("EXCEPTION").val()));
} else {
returnVO.setMsg(properties.getProperty(ReturnCode.valueOf(errorName).msg()));
returnVO.setCode(properties.getProperty(ReturnCode.valueOf(errorName).val()));
}
return returnVO.toString();
}
}
测试代码:
@RequestMapping(value = "/test")
public ReturnVO test() {
throw new RuntimeException("测试非自定义运行时异常");
}
测试结果:
以上两种方法都是针对Controller层的异常进行捕获。针对过滤器或者拦截器的异常,一般处理方法是将异常捕获,并转发到controller,专门处理异常。
举例:
过滤器中
try{
...
} catch (BusinessException e) {
// 解决异常,全局异常处理器捕获不到的问题
request.setAttribute("exception", e);
request.getRequestDispatcher("/business/error").forward(request, response);
}
controller层
@Controller
@RequestMapping(value = "/business")
public class BusinessErrorController {
@RequestMapping("/error")
public void error(HttpServletRequest request) throws BusinessException {
throw (BusinessException) request.getAttribute("exception");
}
}
这样就可以像处理controller层的异常一样处理过滤器和拦截器中的异常了。
参考网址:
Spring @ControllerAdvice vs ErrorController - Stack Overflow