概述
spring提供了构建web应用程序的全功能MVC模块,通过策略接口,spring框架是高度可配置的,而且支持多种视图如:JSP(JavaServer Pages), Velocity,Tiles,iText和POI. SpringMVC分离控制器,模型对象,分派器以及处理程序对象的角色,这种分离让它们更容易进行定制.
SrpingMVC是基于servlet功能实现的,通过实现servlet接口的DispatcherServlet来封装其核心功能实现,通过将请求分派给处理程序,同时带有可配置的处理程序映射,视图解析,本地语言,主题解析以及上传下载文件支持.
ContextLoaderListener
对于spring的功能实现的分析,首先从web.xml开始. 在web.xml一般会配置ContextLoaderListener监听器.一个web都有一个ServletContext,在web启动时创建,在应用关闭时被摧毁,ServletContext在全局范围内有效,类似一个应用中的全局变量. ServletContextListener 在web启动时执行它的实现方法,可以通过该几口向ServletContext添加任意对象.所以ContextLoaderListener,负责在web启动时,自动装配spring的ApplicationContext的配置信息.
ContextLoaderListener首先看web.xml中是否配置了contextClass的值(即指定上下文的类)如果存在则使用用户指定的上下文ApplicationContext,否则使用默认的上下文XmlWebApplicationContext,对该上下文进行实例化并保存到ServletContext中.
DispatcherServlet
ContextLoaderListener只是辅助功能,用于创建WebApplicationContext类型实例,真正的逻辑实现时在DispatcherServlet进行. DispatcherServlet是实现了servlet的实现类.
初始化工作
根据servlet的生命周期可以知道,在servlet的初始化阶段,容器会加载servlet类并调用init方法. DispatcherServlet在init()完成初始化参数init-param的解析和封装,相关配置,spring的WebApplicationContext的初始化即完成xml文件的加载,bean的解析和注册等工作,另外为servlet功能所用的变量进行初始化,如:handlerMapping,viewResolvers等.如下面代码所示,这部分是init核心代码:
1 | // Set bean properties from init parameters. |
servlet中的相关变量,一般时配置在init-param,容器加载完成servlet的实例化过程中会将这些信息解析并存在servelt的config中.所以spring首先是将这些init-param中的变量和值进行解析和封装到PropertyValues. 这些配置信息包括重要的contextConfigLocation(spring xml 文件的位置),contextAttribute(指定WebApplicationContext的类)等.将当前bean封装为BeanWrapper方便后续将上面解析到的属性注入到servlet中的对应变量中.因为xml文件属于Resource需要通过解析器解析配置才能获取相关的bean,所以这里添加了ResourceEditor来解析资源. 完成BeanWrapper的初始化以后进行属性注入.这些都是准备工作,真正的工作交给了initServletBean().这里完成了spring的WebApplicationContext的初始化即完成xml文件的加载,bean的解析和注册等工作,另外为servlet功能所用的变量进行初始化,如:handlerMapping,viewResolvers等.
1 | //*** 初始化上下文环境(xml的加载,bean的解析和注册等),并对servlet功能锁使用的变量进行初始化 |
虽然在ContextLoaderListener完成了ApplicationContxt的配置,但有可能用户没有配置ContextLoaderListener,那么ApplicationContext将是空的.所以spring在ApplicationContext进行初始化之前,还进行了以下工作:首先时判断是否为空,如果不为空说明在ContextLoaderListener已经配置完成.否则尝试从servlet的contextAttribute中获取用户配置的ApplicationContext,如果存在则创建指定的ApplicationContext,否则采用默认的ApplicationContext,即XmlWebApplicationContext.
1 | //说明在ContextLoaderListener中已经配置完成了,则可以直接使用 |
完成配置以后,就需要调用configureAndRefreshWebApplicationContext方法来配置相关信息来完成初始化工作.
1 | wac.setServletContext(getServletContext()); |
为servlet功能所用的变量进行初始化的工作比较简单,主要是加载对应和配置的bean就可以了.
1 | protected void onRefresh(ApplicationContext context) { |
逻辑处理
对于http的不同请求,HttpServlt都提供了相应的服务方法,如:doDelete(),doGet(),doOptions(),doPost(),doPut()和doTrace(). 在DispatcherServlet的这些方法没有做特殊处理,一般都是交给processRequest(request,respone)处理. processRequest的调用过程可以看到DispatcherServlet的请求处理过程是在doDispatch()方法中完成的.
该方法主要完成以下工作:
- spring首先考虑multipart的处理,如果是MultipartContent类型的request,则将该请求转换成MultipartHttpServletRequest类型的request.
- 根据request信息获取对应的Handler. 首先根据request获取访问路径,然后根据该路径可以选择直接匹配或通用匹配的方式寻找Handler. Handler在init()方法时已经完成加载且保存到Map中了,只要根据路径就可以得到对应的Handler. 如果不存在则尝试使用默认的Handler. 如果还是没有找到那么就通过response向用户返回错误信息.找到handler后会将其包装在一个执行链中,然后将所有的拦截器也加入到该链中.
- 如果存在handler则根据当前的handler寻找对应的HandlerAdapter. 通过遍历所有适配器来选择合适的适配器.
- Last-Modified缓存机制的处理
- SpringMVC允许你通过处理拦截器Web请求,进行前置处理和后置处理.所以在正式调用 Handler的逻辑方法时,先执行所有拦截器的preHandle()方法.
- 正式执行handle的业务逻辑方法handle(),逻辑处理是通过适配器调用handle并返回视图.这过程其实是调用用户controller的业务逻辑.
- 调用拦截器的postHandle()方法,完成后置处理.
- 根据视图进行页面跳转.该过程首先会根据视图名字解析得到视图,该过程支持缓存,如果缓存中存在则直接获取,否则创建新的视图并在支持缓存的情况下保存到缓冲中.该过程完成了像添加前缀后缀,设置必须的属性等工作.最后就是进行页面跳转处理.
- 调用拦截器的afterComplection()
1 | try { |