1.自定义全局异常的使用
项目中经常需要自定义全局异常处理器来处理可能发生的异常,实例如下:
1)首先添加异常处理类,添加上@ControllerAdvice,一般也会在类上添加@ResponseBody向调用方返回错误信息,然后添加处理方法,在方法上添加@ExceptionHandler注解指定要处理的异常类型
@ControllerAdvice
@ResponseBody
public class MyExceptionHandler {
@ExceptionHandler(value = ArithmeticException.class)
public Result<String> handleArithmeticException(ArithmeticException e) {
System.out.println("处理ArithmeticException");
return Result.fail("计算异常");
}
}
2)添加控制器类
@RestController
public class Controller01 {
@GetMapping(value = "/test")
public Result<String> test() {
System.out.println(1 / 0);
return Result.ok("1");
}
}
3)添加实体类
@Getter
@Setter
public class Result<T> {
private Integer code;
private String msg;
private T data;
public static <T> Result<T> ok(T data) {
Result<T> result = new Result<>();
result.setCode(0);
result.setData(data);
return result;
}
public static <T> Result<T> fail(String msg) {
Result<T> result = new Result<>();
result.setCode(-1);
result.setMsg(msg);
return result;
}
}
4)添加测试类,并启动
@SpringBootApplication
public class Test {
public static void main(String[] args) {
SpringApplication.run(Test.class, args);
}
}
浏览器调用http://127.0.0.1:8080/test,响应{"code": -1,"msg": "计算异常","data": null}
如果在控制器方法中也添加一个@ExceptionHandler修饰的方法,那么会优先执行控制器中的异常处理方法,响应{"code": -1,"msg": "控制器中计算异常","data": null}
@ExceptionHandler(value = ArithmeticException.class)
public Result<String> handleArithmeticException(ArithmeticException e) {
System.out.println("控制器中处理ArithmeticException");
return Result.fail("控制器中计算异常");
}
2.解析自定义异常处理器
DispatcherServlet初始化时会调用initHandlerExceptionResolvers初始化异常处理解析器,如果没有自定义异常处理解析器,默认会初始化DispatcherServlet.properties配置的类,如下:
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
ExceptionHandlerExceptionResolver的构造函数中会添加默认的消息转换器,因为处理响应时需要依赖消息转换器进行对象转换
public ExceptionHandlerExceptionResolver() {
this.messageConverters = new ArrayList<>();
// ByteArrayHttpMessageConverter支持application/octet-stream、*/*
this.messageConverters.add(new ByteArrayHttpMessageConverter());
// StringHttpMessageConverter支持text/plain、*/*
this.messageConverters.add(new StringHttpMessageConverter());
try {
// SourceHttpMessageConverter支持text/xml、application/xml、application/*-xml.
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
}
// AllEncompassingFormHttpMessageConverter根据加载类的情况添加消息转换器
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
同时ExceptionHandlerExceptionResolver也实现了InitializingBean,初始化时会调用afterPropertiesSet()
public void afterPropertiesSet() {
// 初始化ControllerAdviceBean中的ExceptionHandler
initExceptionHandlerAdviceCache();
// 初始化参数解析器
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 初始化返回值解析器
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 从容器中获取@ControllerAdvice注解修饰的类
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
// 如果有异常处理方法映射,添加到exceptionHandlerAdviceCache集合中
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
// 省略打印日志代码...
}
创建ExceptionHandlerMethodResolver时会解析ControllerAdviceBean中被@ExceptionHandler注解修饰的方法
public class ExceptionHandlerMethodResolver {
// 筛选有@ExceptionHandler注解的方法
public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
// 遍历类中被@ExceptionHandler注解的方法
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
// 遍历@ExceptionHandler注解中定义的每个异常类型
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
// 添加异常类型与异常处理方法的映射关系
addExceptionMapping(exceptionType, method);
}
}
}
}
private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<>();
// 将@ExceptionHandler注解中value的值添加到result集合中
detectAnnotationExceptionMappings(method, result);
if (result.isEmpty()) {
for (Class<?> paramType : method.getParameterTypes()) {
if (Throwable.class.isAssignableFrom(paramType)) {
result.add((Class<? extends Throwable>) paramType);
}
}
}
if (result.isEmpty()) {
throw new IllegalStateException("No exception types mapped to " + method);
}
return result;
}
private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
Assert.state(ann != null, "No ExceptionHandler annotation");
result.addAll(Arrays.asList(ann.value()));
}
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
Method oldMethod = this.mappedMethods.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}
这样,DispatcherServlet初始化时就解析了上述示例中自定义的异常处理器
3.异常处理流程
当服务器接收到请求时,会调用DispatcherServlet#doDispatch执行方法调用,如果调用过程中出现异常,则会进入到异常处理逻辑,调用DispatcherServlet#processHandlerException
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
// 遍历HandlerExceptionResolver,调用resolveException
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
// 省略其他代码...
}
AbstractHandlerExceptionResolver#resolveException源码如下:
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
// 调用doResolveException
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
}
logException(ex, request);
}
return result;
}
else {
return null;
}
}
protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}
然后会调用到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 获取异常处理方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
// 设置参数处理器
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 设置返回值处理器
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable cause = exception.getCause();
if (cause != null) {
// 执行异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// 执行异常处理方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
}
return null;
}
// 如果请求被设置为已处理,直接返回空的ModelAndView
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
// 否则从ModelAndView容器中获取view和model,并构造ModelAndView返回
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
获取异常处理方法逻辑如下:
1)先从当前控制器中查找有没有异常对应的@ExceptionHandler注解修饰的方法
2)如果没有查找到,则从全局异常处理器中查找
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
if (handlerMethod != null) {
// 从exceptionHandlerCache中获取ExceptionHandlerMethodResolver
handlerType = handlerMethod.getBeanType();
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
// 根据handlerType创建ExceptionHandlerMethodResolver
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
// 如果控制器类中有@ExceptionHandler修饰的方法,直接返回此方法对应的ServletInvocableHandlerMethod
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
// 如果当前控制器中没有定义对应的异常处理方法,则从全局异常处理器中查找
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}
执行异常处理方法和执行控制器Handler方法逻辑一样
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 反射执行异常处理方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 通过返回值处理器处理方法执行结果
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
再通过invokeForRequest解析参数后,调用控制器方法执行请求
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取方法执行参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 真正反射执行目标方法
return doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取参数列表
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
// 逐个处理每个参数
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
// 初始化参数名称解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// 从给定参数中寻找,如果能匹配到就不通过参数解析器进行解析了
// providedArgs一般为空,invocableMethod.invokeAndHandle调用时可以传入一些参数
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 判断参数解析器能够支持此参数,这里的参数解析器是HandlerMethodArgumentResolverComposite组合器,会依次匹配每个参数解析器
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 通过参数解析器解析参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
最终调用doInvoke()通过方法反射调用目标控制器方法
protected Object doInvoke(Object... args) throws Exception {
// 如果不是public修饰的方法,则setAccessible(true)
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 通过方法反射调用,getBridgedMethod如果不是桥接的方法,返回的就是方法本身
return getBridgedMethod().invoke(getBean(), args);
}
// 省略异常处理代码...
}