仅供参考后续更新
1、获得处理器handler
比对我们写的controller看那个url与请求url一致
mappedHandler = this.getHandler(processedRequest);
//内部方法是一个for循环进行比对
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
// 在容器启动的时候会自动创建5个handler 这五个中含有自己支持的url
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
2、获得适配器用来调用找到可以处理request中的url所对应的方法
spring mvc有很多这种类似的方法 一个结合相当于一个map容器 容纳一些接口类型的类, 用接口规范好方法, 而用时只需每次调用相同的方法去处理不同的逻辑
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
//内部同样也是一个for循环 看那个支持
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
3、这个是doDispatcher中执行目标方法的东西
//这里我只是先告诉那个方法重要后续会逐渐分开进行讲解
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//利用这个方法执行目标方法在RequestMappingHandlerAdapter.class中
mav = this.invokeHandlerMethod(request, response, handlerMethod);
//进去继续调用这个方法 同时在这个方法中也将处理封装视图方法在下面
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
//注意在上个方法中创建了这个对象这个对象将会是后面一直用的
//同样也是为什么我们放在map、model、modelAndView都会最终放进request域中底层也是个for循环
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//这个是调用我们的目标方法ServletInvocableHandlerMethod.class
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs); 得到我们的返回值
//在以下的方法同时也要注意mavContainer他已经跟着下面的方法一直走同时他也是最后处理和渲染视图要用的
//而这个方法内部最重要的便是我们如何去确定参数 因为利用反射调用目标方法首要的工作便是确定参数, 而类名和方法明 在之前的 handlerAdaptor中就含有了
//这个方法是用来确定参数的方法InvocableHandlerMethod.class
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
MethodParameter[] parameters = this.getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
} else {
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] == null) {
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 var10) {
if (logger.isDebugEnabled()) {
String exMsg = var10.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw var10;
}
}
}
return args;
}
}
//解决参数最重要的便是26个参数解析器 这个方法就是用来寻找参数解析器的
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
//同样跟以前一样都是for循环来判断支持不支持
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
if (result == null) {
Iterator var3 = this.argumentResolvers.iterator();
while(var3.hasNext()) {
HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, resolver);
break;
}
}
}
//以上目标方法便执行完成 还差返回值 这个方法是用来处理返回值的
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
//接着循环找到能够处理返回值的converter
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = this.isAsyncReturnValue(value, returnType);
Iterator var4 = this.returnValueHandlers.iterator();
HandlerMethodReturnValueHandler handler;
do {
do {
if (!var4.hasNext()) {
return null;
}
handler = (HandlerMethodReturnValueHandler)var4.next();
} while(isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler));
} while(!handler.supportsReturnType(returnType));
return handler;
}
//这两个是内容协商的东西 浏览器可以支持的媒体类型, 以及服务器尽自己的能可以出来的媒体类型
acceptableTypes = this.getAcceptableMediaTypes(request);
List<MediaType> producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);
//acceptableTypes 是通过内容协商去得到浏览器的Accept字段或者如果打开了参数
//fovor-param=true, 还将会添加里一个内容协商参数
//接下来就是找到可用的媒体类型 利用httpmessageConveters进行遍历找到那个支持媒体类型
List<MediaType> mediaTypesToUse = new ArrayList();
Iterator var15 = acceptableTypes.iterator();
MediaType mediaType;
while(var15.hasNext()) {
mediaType = (MediaType)var15.next();
Iterator var17 = producibleTypes.iterator();
while(var17.hasNext()) {
MediaType producibleType = (MediaType)var17.next();
if (mediaType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));
}
}
}
//视图处理流程
SpringMvc对于错误的处理办法
首先我们来看springmvc会有一个错误的自动配置 ErrorMvcAutoConfiguration
这个自动配置类将会加载三个组件
- BasicErrorController 当错误没有人来处理的情况下都会交由这个 controller处理, 也就是我们看到的最普通的白页。
- DefaultErrorAttributes
- DefaultErrorViewResolver
下面来进行详细的讲解
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
这个方法也就是解析视图的方法同时会把错误传进去
// 进去的方法 如果传的错误不为空将会进入这个方法最重要便是 mv=this.process
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
//开始遍历错误视图解析器
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
第一个错误视图解析器就是我们刚才放的 DefaultErrorAttributes解析器
其实不能称它为解析器因为他每次必然会执行(因为order最低)他只是给我们把异常放入request域中之后便返回空的视图。
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
this.storeErrorAttributes(request, ex);
return null;
}
private void storeErrorAttributes(HttpServletRequest request, Exception ex) {
request.setAttribute(ERROR_ATTRIBUTE, ex);
}
而第二个视图解析器是一个解析器集合这个是真正来处理我们错误的解析器
HandlerExceptionResolverComposite
这个就是上面解析器的集合,
0 -> 这个是用来处理我们使用了 ControllerAdvice加强并且标注了ExceptionHandler注解的解析器
他将会在容器启动的时候自动将我们标了 exceptionHandler 注解的方法扫描进去再里面进行执行判断逻辑便是下面这个方法
大概就是 先找到能够处理当前异常类的方法
接着回到以前的步骤确定参数
利用反射进行调用我们的方法
返回跟我们之前一样视图处理的mavContain
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
} else {
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();
ArrayList exceptions = new ArrayList();
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable cause;
for(Object exToExpose = exception; exToExpose != null; exToExpose = cause != exToExpose ? cause : null) {
exceptions.add(exToExpose);
cause = ((Throwable)exToExpose).getCause();
}
Object[] arguments = new Object[exceptions.size() + 1];
exceptions.toArray(arguments);
arguments[arguments.length - 1] = handlerMethod;
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
} catch (Throwable var13) {
if (!exceptions.contains(var13) && this.logger.isWarnEnabled()) {
this.logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, var13);
}
return null;
}
if (mavContainer.isRequestHandled()) {
return new 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 -> 这个是用来处理我们自定义的运行时异常, 必须在我们自定义的异常类中标注repsonseStatus(第一个参数时状态码, 第二个时原因)
protected ModelAndView resolveResponseStatusException(ResponseStatusException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception {
ex.getResponseHeaders().forEach((name, values) -> {
values.forEach((value) -> {
response.addHeader(name, value);
});
});
return this.applyStatusAndReason(ex.getRawStatusCode(), ex.getReason(), response);
}
protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response) throws IOException {
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);
} else {
String resolvedReason = this.messageSource != null ? this.messageSource.getMessage(reason, (Object[])null, reason, LocaleContextHolder.getLocale()) : reason;
response.sendError(statusCode, resolvedReason);
}
return new ModelAndView();
}
2 -> 这个是用来处理spring底层出现的错误 例如什么 传的参数类型不对等等
后两个方法也是执行response.sendError() 这个方法
并返回一个modelandview对象 但是对象中什么都没有
这三个方法都处理不了交给tomcat tomcat也是发送 response.sendError
发送/error请求
这个请求将会被执行写的那个默认basicController处理而他的适配器也是requestMappingAadpt
当然我们自己也能编写自己的处理器 也就是实现handlerExceptionresovler
接口并加入容器中注意要把我们的处理器的order