spring源码剖析(九)springMVC源码剖析
来源:互联网 发布:可以制作手机壁纸软件 编辑:程序博客网 时间:2024/05/17 19:21
springMVC 相信大伙都用过,但是spring框架对于你请求的一个url 到你看到的返回结果,期间做了哪些出来呢,文件上传的封装?controller的寻找?过滤器的调用?AOP的调用?视图的解析?页面的跳转? 这些过程具体是怎么实现的,下面我们一一来向大家介绍springMVC的架构。
让我们来先从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方法,如下:
@Overridepublic 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的配置//传递回不同的ControllerinitHandlerMappings(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类型的requestprocessedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 根据request信息,寻找具体的处理handlermappedHandler = getHandler(processedRequest, false);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// 根据当前对的handler 查找对应的handlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//处理 带last-modified的headerString 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 afterCompletionif (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的大体初始化逻辑,和处理逻辑已经分析完了,以上的描述,如有不对,请各位大神指出,谢谢~
0 0
- spring源码剖析(九)springMVC源码剖析
- Atlas源码剖析(九)
- Lucene 源码剖析 九
- spring源码深度剖析(自我剖析)
- SpringMVC源码剖析
- spring源码剖析分享
- Spring AOP源码剖析
- Spring MVC源码剖析
- spring jdbcTemplate源码剖析
- spring jdbcTemplate源码剖析
- Spring MVC源码剖析
- libevent源码深度剖析九
- libevent源码深度剖析九
- libevent源码深度剖析九
- libevent源码深度剖析九
- libevent源码深度剖析九
- libevent源码深度剖析九
- libevent源码深度剖析九
- maven3常用命令、java项目搭建、web项目搭建详细图解
- %r和%s该用哪一个
- Ceph 集群 client 节点 rdb配置和使用
- Codeforces Round #401 (Div. 2) A---E
- Rabbitmq集群HA高可用环境部署
- spring源码剖析(九)springMVC源码剖析
- N皇后问题扩张(算法训练 王 后问题)
- 最小生成树--九度1017[Kruskal]
- angular ng-cloak解决屏幕闪烁问题
- Android Studio使用心得
- Spring4.0.2使用p6spy打印mybatis执行SQL
- 数据结构——树(1):树的基本概念,二叉树、完全二叉树、满二叉树、搜索二叉树
- ConstraintLayout约束控件详解
- qt4.8+Phonon播放.wav声音文件