@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处理异常的大致原理
- ExceptionHandlerExceptionResolver是Spring MVC缺省被启用的一个HandlerExceptionResolver,它会被作为一个组合模式HandlerExceptionResolver bean中的一个元素进入到bean容器中;ExceptionHandlerExceptionResolver在初始化时会收集@ExceptionHandler以供使用
- DispatcherServlet初始化时,会搜集所有HandlerExceptionResolver bean记录到自己的策略组件属性List handlerExceptionResolvers
- 当异常发生时,DispatcherServlet会遍历handlerExceptionResolvers中每个HandlerExceptionResolver对象试图对该异常进行处理。
- ExceptionHandlerExceptionResolver处理异常
- 先从发生异常的控制器方法所在类查找是否存在使用注解@ExceptionHandler并能处理该异常的方法
- 如果找不到,从所有@ControllerAdvice注解类中查找使用注解@ExceptionHandler并能处理该异常的方法;
参考文章
SpringMvc @InitBinder: https://www.cnblogs.com/lvbinbin2yujie/p/10459303.html
ControllerAdvice文档
SpringBoot源码解析-ExceptionHandler处理异常的原理
Spring Web : 注解@ExceptionHandler的工作原理