spring源码剖析(九)springMVC源码剖析
2017-02-26 14:02
246 查看
springMVC 相信大伙都用过,但是spring框架对于你请求的一个url 到你看到的返回结果,期间做了哪些出来呢,文件上传的封装?controller的寻找?过滤器的调用?AOP的调用?视图的解析?页面的跳转? 这些过程具体是怎么实现的,下面我们一一来向大家介绍springMVC的架构。
让我们来先从SpringMVC的初始化说起吧
一般来说我们配置spring的时候都会在web.xml里面加上下面这段配置:
我们先来看看ContextLoaderListener的类继承的关系
我们看到ContextLoaderListener 继承了ServletContextListener ,由于系统启动的时候会回调ServletContextListener 的contextInitialized()方法, 所以我们可以知道,当容器启动的时候就会调用ContextLoaderListener 的contextInitialized()方法,如下:
上面这段回调的具体作用在用,项目启动的时候让系统去初始化WebApplicationContext,至于为什么要初始化WebApplicationContext呢,因为我们的DispatcherServlet需要用到它。
先浏览DispatcherServlet的初始化逻辑时序图:
我们看到DispatcherServlet继承了HttpServlet类,关于HttpServlet,让我们来回顾一下普通的HttpServlet的生命周期
1)初始化阶段
servlet容器加载servlet类,把Servlet的.class文件中的数据读到内存中
servlet容器创建一个ServletConfig对象,ServletConfig对象包含了servlet的初始化配置信息。
servlet容器创建一个servlet对象。
servlet容器调用servlet对象的init方法进行初始化。
2)运行阶段
当servlet容器接受到一个请求的时候,servlet容器会创建servletRequest和servletResponse对象,然后调用service方法。
3)销毁阶段
当Web应用被终止时候,servlet容器会先调用servlet对象的destroy方法,然后再销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象。
由于DispatcherServlet继承了HttpServlet类,所以当DispatcherServlet初始化的时候,首先会调用它的init方法。我们再HttpServletBean找到了init方法,如下:
这里的关键方法在于initServletBean(),这个方法里面主要是为了初始化WebApplicationContext所做的操作,下面我们来看看里面比较这个方法里面调用得到的比较重要的方法,跟着initServletBean调用的调用链,我们找到DispatcherServlet的initStrategies方法:
上面的初始化完成,那就说明DispatcherServlet已经可以开始 工作了,下面,让我看看DispatcherServlet的处理逻辑
其实delete,options,trace以及put的请求方式都一样,都是最终会转交到processRequest方法去处理。
让我们看看大体的处理逻辑时序图:
下面我们来看看核心的处理逻辑代码doDispatch()的具体实现吧:
下面我们来一一解析,具体实施过程:
当spring初始化的时候,会把所有的控制器(controller)都加载到HandlerMapper 里面去,然后根据Request里面请求的url,定位到具体的控制器(controller)里面,然后返回handler,返回的handler,包含了拦截器执行的调用链。
让我们再看看ha.support()是判断什么的:
如果适配器属于右侧中类型指定的适配器,那么就可以返回给程序使用,当然,里面我们最常用的就是controller类。具体实现大家有兴趣的可以深入的看看调用执行链。
好了,DispatcherServlet的大体初始化逻辑,和处理逻辑已经分析完了,以上的描述,如有不对,请各位大神指出,谢谢~
让我们来先从SpringMVC的初始化说起吧
SpringMVC的初始化
ContextLoaderListener初始化
我们先看看ContextLoaderListener大体的初始化逻辑时序图:一般来说我们配置spring的时候都会在web.xml里面加上下面这段配置:
<!--Spring ApplicationContext 载入 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>添加这段配置的目的在于,启动项目的时候让系统去回调ContextLoaderListener的contextInitialized()方法。
我们先来看看ContextLoaderListener的类继承的关系
我们看到ContextLoaderListener 继承了ServletContextListener ,由于系统启动的时候会回调ServletContextListener 的contextInitialized()方法, 所以我们可以知道,当容器启动的时候就会调用ContextLoaderListener 的contextInitialized()方法,如下:
/** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); }
上面这段回调的具体作用在用,项目启动的时候让系统去初始化WebApplicationContext,至于为什么要初始化WebApplicationContext呢,因为我们的DispatcherServlet需要用到它。
DispatcherServlet的初始化
让我们先看看DispatcherServlet的类继承结构先先浏览DispatcherServlet的初始化逻辑时序图:
我们看到DispatcherServlet继承了HttpServlet类,关于HttpServlet,让我们来回顾一下普通的HttpServlet的生命周期
1)初始化阶段
servlet容器加载servlet类,把Servlet的.class文件中的数据读到内存中
servlet容器创建一个ServletConfig对象,ServletConfig对象包含了servlet的初始化配置信息。
servlet容器创建一个servlet对象。
servlet容器调用servlet对象的init方法进行初始化。
2)运行阶段
当servlet容器接受到一个请求的时候,servlet容器会创建servletRequest和servletResponse对象,然后调用service方法。
3)销毁阶段
当Web应用被终止时候,servlet容器会先调用servlet对象的destroy方法,然后再销毁servlet对象,同时也会销毁与servlet对象相关联的servletConfig对象。
由于DispatcherServlet继承了HttpServlet类,所以当DispatcherServlet初始化的时候,首先会调用它的init方法。我们再HttpServletBean找到了init方法,如下:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. try { //解析init-param参数,并封装到pvs中 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); //将当前的servlet类转换成bw类,目的是能配合init-param进行使用 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); //注册自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); //空实现,留给子类覆盖 initBeanWrapper(bw); //属性注入 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } //留给子类扩展 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
这里的关键方法在于initServletBean(),这个方法里面主要是为了初始化WebApplicationContext所做的操作,下面我们来看看里面比较这个方法里面调用得到的比较重要的方法,跟着initServletBean调用的调用链,我们找到DispatcherServlet的initStrategies方法:
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { //初始化MultipartResolver,这个主要是用来处理文件上传的 initMultipartResolver(context); //初始化LocaleResolver,这个主要用来配置国际化的。 initLocaleResolver(context); //初始化ThemeResolver,用于用控制不同的页面风格 initThemeResolver(context); //初始化HandlerMappings,客户端发出request时候,DispatcherServlet会将 //request提交给handlerMappers,然后handlerMappers根据WebApplicationContext的配置 //传递回不同的Controller initHandlerMappings(context); //初始化适配器 initHandlerAdapters(context); //异常处理 initHandlerExceptionResolvers(context); //当处理器没有返回View对象获取视图名称,并且没有往response里面写数据的时候, //spring就会采用约定好的方式提供一个逻辑视图名称 initRequestToViewNameTranslator(context); //初始化视图解析器 initViewResolvers(context); //Srping提供了一个请求存储属性,可以提供给其他请求使用。 initFlashMapManager(context); }
上面的初始化完成,那就说明DispatcherServlet已经可以开始 工作了,下面,让我看看DispatcherServlet的处理逻辑
DispatcherServlet处理逻辑
http有很多请求方式,有delete,post,get,options,trace,put这些方法,servlet是怎么处理这些方法的呢,让我们来看看FrameworkServlet的源代码;protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
其实delete,options,trace以及put的请求方式都一样,都是最终会转交到processRequest方法去处理。
让我们看看大体的处理逻辑时序图:
下面我们来看看核心的处理逻辑代码doDispatch()的具体实现吧:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { //如果是multipartContext类型的request,则转换为MultipartHttpServletRequest类型的request processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根据request信息,寻找具体的处理handler mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 根据当前对的handler 查找对应的handlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //处理 带last-modified的header String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 真正激活handler 并返回视图 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //处理默认的视图名称,就是替视图加上前后缀 applyDefaultViewName(request, mv); //interceptor的调用 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; }//处理返回结果 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
下面我们来一一解析,具体实施过程:
MultipartContont 类型的request处理
如果处理的request带有文件上传处理,那么就会转换成MultipartHttpServletRequest类型的request根据request信息,寻找具体的处理handler
这个里面的实现过程稍稍复杂一点,不过大体的逻辑还是不变的,具体是:当spring初始化的时候,会把所有的控制器(controller)都加载到HandlerMapper 里面去,然后根据Request里面请求的url,定位到具体的控制器(controller)里面,然后返回handler,返回的handler,包含了拦截器执行的调用链。
根据当前对的handler 查找对应的handlerAdapter
handlerAdapter也是在DispatcherServlet初始化的时候已经把所有的Adapter初始化好了,放到HandlerAdaptes里面去了,有人也许会问adapter是什么啊?我们看看具体的实现就知道了,下面是获取adapter的具体实现:protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
让我们再看看ha.support()是判断什么的:
如果适配器属于右侧中类型指定的适配器,那么就可以返回给程序使用,当然,里面我们最常用的就是controller类。具体实现大家有兴趣的可以深入的看看调用执行链。
处理 带last-modified的header
如果处理器实现了LastModified接口,那么就可以做到缓存的效果,具体是:如果页面第一次请求成功了,在发起第二次请求的时候,如果请求头包含的时间与服务器端的返回的lastmodified时间一致,那么就会返回HTTP304的状态吗(只需要响应头,内容为空,这样就节省了带宽)。真正激活handler 并返回视图
这一步骤 具体的话就是调用用户的逻辑处理了。处理默认的视图名称,就是替视图加上前后缀
在spring配置的视图解析器里面,我们一般会配置返回视图的前缀以及视图指定的后缀名称,这一步骤的目的就是根据返回的视图配置前后缀的名称。interceptor的调用
在这里,执行拦截器的调用,下面是具体实现:/** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } }
处理返回结果
根据逻辑处理返回的结果,执行跳转,或者response的回写。好了,DispatcherServlet的大体初始化逻辑,和处理逻辑已经分析完了,以上的描述,如有不对,请各位大神指出,谢谢~
相关文章推荐
- 深入剖析Spring Web源码(三) - Spring Web MVC工作流
- 深入剖析Spring Web源码(五) - DispatcherServlet的实现 - 通用Servlet和HTTP Servlet
- 深入剖析Spring Web源码(十一) - 处理器映射,处理器适配器以及处理器的实现 - 处理器映射的实现架构
- SpringMVC源码剖析(三)- DispatcherServlet的初始化流程
- SpringMVC源码剖析(二)- DispatcherServlet的前世今生
- 深入剖析Spring Web源码(一) - 前言
- SpringMVC源码剖析(三)- DispatcherServlet的初始化流程
- 深入剖析Spring Web源码(十九) - 整理的文档和日志的索引(第一版)
- 深入剖析Spring Web源码(十八) - 视图解析和视图显示 - 更多的视图解析器
- SpringMVC源码剖析(一)- 从抽象和接口说起
- 深入剖析Spring Web源码(六) - DispatcherServlet的实现 - 派遣器Servlet及其父类
- 深入剖析Spring Web源码(十一) - 处理器映射,处理器适配器以及处理器的实现 - 处理器映射的实现架构
- 深入剖析Spring Web源码(十) - 处理器映射,处理器适配器以及处理器的实现 - 基于HTTP请求处理器流程的实现
- 深入剖析Spring Web源码(二十) - Spring安全 - 横向剖析
- 深入剖析Spring Web源码(十五) - 处理器映射,处理器适配器以及处理器的实现 - 处理器的实现架构 - HTTP请求处理器
- 深入剖析Spring Web源码(四) - DispatcherServlet的实现
- 深入剖析Spring Web源码(九) - 处理器映射,处理器适配器以及处理器的实现 - 基于注解控制器流程的实现
- 深入剖析Spring Web源码(十三) - 处理器映射,处理器适配器以及处理器的实现 - 处理器的实现架构 - 简单控制器
- 关于Spring中的AOP和事物的源码剖析
- SpringMVC源码剖析(二)- DispatcherServlet的前世今生