SpringMVC默认加载三个请求处理映射类:RequestMappingHandlerMapping、SimpleUrlHandlerMapping、和BeanNameUrlHandlerMapping。
这三个类有一个共同的父类:AbstractHandlerMapping。在上面代码中hm.getHandler(request)这个getHandler方法在AbstractHandlerMapping中,它的子类都没有重写这个方法。因此我们含有必要去AbstractHandlerMapping这个类中看一下这个方法:
@Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 这个是留给子类去重写实现的:查找handler处理器的~ 比如根据URL去查找匹配等等 // 备注:获取hadnler的过程,非常的复杂,这个必须后面单独的专题再说吧 Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } //构建出一个处理器链 注意:和handler绑定了,并且内部还去拿到了所有的拦截器,然后添加到处理器连里面去 getHandlerExecutionChain() 方法自己去看,可以看明白 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); //是不是cors请求,cors是跨域请求 if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
HandlerExecutionChain#applyPreHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; // 注意:如果是拦截器返回了false,就立马触发所有拦截器的AfterCompletion 方法。并且马上return false if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; //如果有异常,就进入异常处理逻辑,返回到异常页面 if (exception != null) { // 含有异常页面视图的异常 if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); //1、会执行所有的我们的自己配置(或者默认配置)了的HandlerExceptionResolver处理器 //2、上面需要注意了,但凡处理方法返回的不是null,有mv的返回。那后面的处理器就不会再进行处理了。具有短路的效果,一定要注意 是通过null来判断的 //3、处理完成后,得到error的视图mv,最后会设置一个viewName,然后返回出去 mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // 若视图不为空,不为null,就开始执行render()方法,开始渲染视图了 if (mv != null && !mv.wasCleared()) { render(mv, request, response); // 如果有错误视图,这里清除掉所有的请求域里的所有的错误属性 if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } //处理异步=========我们发现,它不执行后面的AfterCompletion方法了,注意一下即可 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } // 执行拦截器的AfterCompletion 方法 if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
至此,只剩一个视图渲染的方法:render()
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { // 通过localeResolver吧local解析出来,放到response里面去 Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale()); response.setLocale(locale); //==================视图:关键中的关键================== View view; String viewName = mv.getViewName(); // 如果已经有viewName了(绝大多数情况) if (viewName != null) { // 视图解析器 根据String类型的名字,解析出来一个视图(视图解析器有多个) // 还是那个原理:只要有一个返回了不为null的,后面的就不会再解析了 view = resolveViewName(viewName, mv.getModelInternal(), locale, request); // 如果解析不出来视图,那就抛出异常,说不能解析该视图 if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { //没有视图名称,但是必须有视图内容,否则抛出异常 view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } try { //设置响应马 status if (mv.getStatus() != null) { response.setStatus(mv.getStatus().value()); } // 根据model里的数据,正式渲染(关于此部分逻辑,后续再说,也比较复杂) view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { throw ex; } }
至此,整个Spring MVC处理请求的一个过程算是结束了
总结
本文从FrameworkServlet、DispatcherServlet源码处去跟踪比较具象的去描述了Spring MVC的一个执行过程。从中我们可以深刻感受到,Spring在考虑这些问题的时候还是非常全面的。
还能感受它里面使用得丝滑般的设计模式:门面模式、模版模式等等~~
Spring MVC作为现在Java Web开发中实际的规范,大多数时候我们只需要着眼关注我们自己书写的Controller本身了,但是如果我们想做一些优雅处理:全局异常处理、数据绑定处理、序列化反序列化定制化处理等等,理解这些工作流程,现在就如有神助了