Spring源码学习--springMVC

概述

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Set bean properties from init parameters.
try {
//解析init-param 并封装在 pvs中 ***
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

//将当前这个servlet转化为一个BeanWrapper,从而能够以Spring的方式来对init-param的值进行注入
//保证了spring能够将这些参数注入到对应的值中
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());

//注册自定义属性编辑器,一旦遇到Resource类型的属性将会使用ResourceEditor进行解析
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//空实现,留给子类覆盖 (模板方法)
initBeanWrapper(bw);
//属性注入,如congtextAttribute,contextClass,nameSpace,contextConfigLocation
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}

// Let subclasses do whatever initialization they like.
//留给子类扩展 **** FrameworkServlet
initServletBean();

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
2
//*** 初始化上下文环境(xml的加载,bean的解析和注册等),并对servlet功能锁使用的变量进行初始化
this.webApplicationContext = initWebApplicationContext();

虽然在ContextLoaderListener完成了ApplicationContxt的配置,但有可能用户没有配置ContextLoaderListener,那么ApplicationContext将是空的.所以spring在ApplicationContext进行初始化之前,还进行了以下工作:首先时判断是否为空,如果不为空说明在ContextLoaderListener已经配置完成.否则尝试从servlet的contextAttribute中获取用户配置的ApplicationContext,如果存在则创建指定的ApplicationContext,否则采用默认的ApplicationContext,即XmlWebApplicationContext.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//说明在ContextLoaderListener中已经配置完成了,则可以直接使用
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
//context实例在构造函数中被注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}

//刷新上下文环境 ***
configureAndRefreshWebApplicationContext(cwac);
}
}
}
//假如说 没有配置ContextLoaderListener 或在ContextLoaderListener中尚未完成
//WebApplicationContext的配置工作那么在servlet的变量环境中寻找
if (wac == null) {
// 根据contextAttribute属性加载WebAppliactionContext
wac = findWebApplicationContext();
}
//如果都没配置,则使用默认的 上下文 XmlWebApplicationContext
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}

if (!this.refreshEventReceived) {
//为servlet功能所用的变量进行初始化,如:handlerMapping,viewResolvers等
onRefresh(wac);
}

完成配置以后,就需要调用configureAndRefreshWebApplicationContext方法来配置相关信息来完成初始化工作.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wac.setServletContext(getServletContext());
//将servlet中将解析到的配置信息 进行注入
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}

postProcessWebApplicationContext(wac);
applyInitializers(wac);

//加载配置文件及整合parent到wac
wac.refresh();

为servlet功能所用的变量进行初始化的工作比较简单,主要是加载对应和配置的bean就可以了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//初始化MultipartResolver 主要用于文件的上,默认没有提供. 内部是尝试加载该bean实例
initMultipartResolver(context);
//初始化LocaleResolver. 国际化
initLocaleResolver(context);
//初始化ThemeResovler, web经常会通过主题Theme来控制网页风格.
initThemeResolver(context);
/*
* 以上的实现原理一样,都是直接从容器中加载对应的bean.
* 因为context 在前面已经完成初始化,即完成了bean的解析和注册.
* ***************************************************************************
* ***************************************************************************
* 下面的实现原理相同,首先有个detectAllXXX的属性来决定是否从容器中获取所有的该类型的bean,
* 如果没有设置看用户是否指定了具体的bean,如果指定了则使用该bean
* 否则,使用默认的配置bean
*/
//初始化HandlerMappings spring发出request请求时,DispatcherServlet会将request交给HandlerMapping,
//然后HandlerMapping根据Web ApplicationContext 来配置来回传给DispatcherServlet相应的Controller ***
initHandlerMappings(context);
// 适配器 **
initHandlerAdapters(context);
// 异常处理
initHandlerExceptionResolvers(context);
// 视图名字转化器
initRequestToViewNameTranslator(context);
// 视图解析器
initViewResolvers(context);
//
initFlashMapManager(context);
}

逻辑处理

对于http的不同请求,HttpServlt都提供了相应的服务方法,如:doDelete(),doGet(),doOptions(),doPost(),doPut()和doTrace(). 在DispatcherServlet的这些方法没有做特殊处理,一般都是交给processRequest(request,respone)处理. processRequest的调用过程可以看到DispatcherServlet的请求处理过程是在doDispatch()方法中完成的.
该方法主要完成以下工作:

  1. spring首先考虑multipart的处理,如果是MultipartContent类型的request,则将该请求转换成MultipartHttpServletRequest类型的request.
  2. 根据request信息获取对应的Handler. 首先根据request获取访问路径,然后根据该路径可以选择直接匹配或通用匹配的方式寻找Handler. Handler在init()方法时已经完成加载且保存到Map中了,只要根据路径就可以得到对应的Handler. 如果不存在则尝试使用默认的Handler. 如果还是没有找到那么就通过response向用户返回错误信息.找到handler后会将其包装在一个执行链中,然后将所有的拦截器也加入到该链中.
  3. 如果存在handler则根据当前的handler寻找对应的HandlerAdapter. 通过遍历所有适配器来选择合适的适配器.
  4. Last-Modified缓存机制的处理
  5. SpringMVC允许你通过处理拦截器Web请求,进行前置处理和后置处理.所以在正式调用 Handler的逻辑方法时,先执行所有拦截器的preHandle()方法.
  6. 正式执行handle的业务逻辑方法handle(),逻辑处理是通过适配器调用handle并返回视图.这过程其实是调用用户controller的业务逻辑.
  7. 调用拦截器的postHandle()方法,完成后置处理.
  8. 根据视图进行页面跳转.该过程首先会根据视图名字解析得到视图,该过程支持缓存,如果缓存中存在则直接获取,否则创建新的视图并在支持缓存的情况下保存到缓冲中.该过程完成了像添加前缀后缀,设置必须的属性等工作.最后就是进行页面跳转处理.
  9. 调用拦截器的afterComplection()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
try {
//如果时MultipartContent类型的request 则转换request 为MultipartHttpServletRequest类型的request **
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
//根据request信息寻找对应的handler **
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果没有找到对应的handler则通过response反馈错误信息
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
//根据当前的handler寻找对应的handlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
//如果当前handler支持last-modified头处理
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;
}
}
//*** applyPreHandle
// 拦截器的preHandler方法的调用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
//主要的逻辑处理
//真正激活handler并返回视图
//默认是 SimpleControllerHandlerAdapter 类
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//视图名称转换应用于需要添加前缀后缀的情况 **
applyDefaultViewName(processedRequest, mv);

//应用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//对结果进行处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
0%