@RestControllerAdvice

@RestControllerAdvice介绍

官方文档

作为的特殊化@Component,允许通过类路径扫描自动检测实现类。 通常用于定义@ExceptionHandler,
@InitBinder和@ModelAttribute 适用于所有@RequestMapping方法的方法。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] assignableTypes() default {};

    Class<? extends Annotation>[] annotations() default {};
}
  • value
@AliasFor(value="basePackages")
public abstract String[] value

basePackages包名

例如: @ControllerAdvice("org.my.pkg")等价于 @ControllerAdvice(basePackages="org.my.pkg")。
  • basePackages
@AliasFor(value="value")
public abstract String[] basePackages

basePackages包数组

 e.g.:
  @ControllerAdvice(basePackages="org.my.pkg") 
  or @ControllerAdvice(basePackages={"org.my.pkg", "org.my.other.pkg"}).  

我们常用的就是value(),basePackages(),来限定控制器;annotations(),basePackageClasses()也能。

@InitBinder

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InitBinder {
    String[] value() default {};
}

该注解用于标识初始化{@link org.springframework.web.bind.WebDataBinder}的方法,该方法将用于填充带注释的处理程序方法的命令和表单对象参数。我们可以理解为将Web请求中的属性转成我们需要的类型。

@ModelAttribute

该注解的方法可以处理Model中的数据;

@ExceptionHandler

这个注解就是我们最常用的呢;用来进行统一异常处理。

@RestControllerAdvice
public class BaseExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public Result exception(Exception e){
        return new Result(StatusCode.ERROR,false,e.getMessage());
    }
}

异常到达@ExceptionHandler的基本流程
在这里插入图片描述

InvocableHandlerMethod.doInvoke():可以捕获到异常,并抛出异常throw (RuntimeException) targetException;

DispatcherServlet.doDispatch():处理异常

DispatcherServlet.processDispatchResult():处理程序调用的结果;该结果可以是ModelAndView或要解析为ModelAndView的Exception;如果exception != null,

/**
	 * Handle the result of handler selection and handler invocation, which is
	 * either a ModelAndView or an Exception to be resolved to a ModelAndView.
	 */
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;
		// 如果exception != null
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
		  	// 初始化handler 
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				//处理异常
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

DispatcherServlet.processHandlerException():通过注册的HandlerExceptionResolvers确定错误ModelAndView。
HandlerExceptionResolverComposite.resolveException() 通过遍历已配置的异常解析器列表来解决异常。 第一个返回ModelAndView实例的对象将获胜。否则,返回{@code null}
AbstractHandlerExceptionResolver.resolveException():它会先检查此解析器是否应该应用于给定的处理程序。
AbstractHandlerExceptionResolver.resolveException

@Override
	@Nullable
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) {
		//检查此解析器是否应该应用于给定的处理程序
		if (shouldApplyTo(request, handler)) {
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
			}
			prepareResponse(ex, response);
			//符合,进行处理
			ModelAndView result = doResolveException(request, response, handler, ex);
			if (result != null) {
				logException(ex, request);
			}
			return result;
		}
		else {
			return null;
		}
	}

ExceptionHandlerExceptionResolver.doResolveHandlerMethodException():找到一个{@code @ExceptionHandler}方法并调用它来处理引发的异常

ServletInvocableHandlerMethod.invokeAndHandle():获得返回数据。
InvocableHandlerMethod.doInvoke():使用给定的参数值调用处理方法;
@ExceptionHandler:自定义处理异常

核心类ExceptionHandlerExceptionResolver
initExceptionHandlerAdviceCache方法:找出所有的@ControllerAdvice下的@ExceptionHandler;放入到exceptionHandlerAdviceCache中

	private void initExceptionHandlerAdviceCache() {
		if (getApplicationContext() == null) {
			return;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for exception mappings: " + getApplicationContext());
		}
	// 找出所有的ControllerAdvice
		List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
		AnnotationAwareOrderComparator.sort(adviceBeans);
		
	// 遍历ControllerAdvice
		for (ControllerAdviceBean adviceBean : adviceBeans) {
			Class<?> beanType = adviceBean.getBeanType();
			if (beanType == null) {
				throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
			}
			//找出所有的@ExceptionHandler
			ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
			if (resolver.hasExceptionMappings()) {
			//  放入到 Map<ControllerAdviceBean, ExceptionHandlerMethodResolver>
				this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
				if (logger.isInfoEnabled()) {
					logger.info("Detected @ExceptionHandler methods in " + adviceBean);
				}
			}
			if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				this.responseBodyAdvice.add(adviceBean);
				if (logger.isInfoEnabled()) {
					logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
				}
			}
		}
	}

ExceptionHandler处理异常的大致原理

  1. ExceptionHandlerExceptionResolver是Spring MVC缺省被启用的一个HandlerExceptionResolver,它会被作为一个组合模式HandlerExceptionResolver bean中的一个元素进入到bean容器中;ExceptionHandlerExceptionResolver在初始化时会收集@ExceptionHandler以供使用
  2. DispatcherServlet初始化时,会搜集所有HandlerExceptionResolver bean记录到自己的策略组件属性List handlerExceptionResolvers
  3. 当异常发生时,DispatcherServlet会遍历handlerExceptionResolvers中每个HandlerExceptionResolver对象试图对该异常进行处理。
  4. ExceptionHandlerExceptionResolver处理异常
    1. 先从发生异常的控制器方法所在类查找是否存在使用注解@ExceptionHandler并能处理该异常的方法
    2. 如果找不到,从所有@ControllerAdvice注解类中查找使用注解@ExceptionHandler并能处理该异常的方法;

参考文章

SpringMvc @InitBinder: https://www.cnblogs.com/lvbinbin2yujie/p/10459303.html

ControllerAdvice文档
SpringBoot源码解析-ExceptionHandler处理异常的原理
Spring Web : 注解@ExceptionHandler的工作原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章