Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5651字,纯文字阅读大概需要9分钟。
内容图文
![Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程](/upload/InfoBanner/zyjiaocheng/1276/e658531990be4c5eafb07fd81d738c10.jpg)
Spring版本: 4.0.X
注:这里的分析只关注整个处理流程的大致过程,省略与流程无关的代码。
应用根上下文(Root ApplicationContext)的启动
我们知道在一个web项目中使用SpringMVC时,需在web.xml
中配置一个监听器:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>
因此ContextLoaderListener
就是我们分析的入口点。这个类的承继结构如下:
在该类中我们发现有private WebApplicationContext context
的声明,可见这个类实际持有了容器对象,通过spring给出的注释可知这是整个应用的根上下文。那么这个WebApplicationContext
具体实现类是哪个,又是在哪里实例化的呢?
当部署应用到应用服务器中时,应用服务器首先会调用监听器的contextInitialized()
方法,于是我们去ContextLoaderListener
的contextInitialized()
方法中查看,发现只有一行调用,即:
initWebApplicationContext(event.getServletContext());
这个方法在其父类ContextLoader
中实现。进入该方法,发现有如下代码调用:
try {
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
// ......
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// ......return this.context;
}
catch (RuntimeException ex) {
/// ......
}
可见,createWebApplicationContext()
方法负责创建的容器,创建完成后,Spring将该根容器的引用放到了ServletContext
中以便于后续读取使用。在WebApplicationContext
接口中可以看到ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
的定义如下:
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
也就是类的全限定名加上.ROOT后缀。
我们深入到createWebApplicationContext()
方法中,代码大致如下:
Class<?> contextClass = determineContextClass(sc);
// ......return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
可知Spring是通过反射来创建容器类的(废话)。在determineContextClass()
方法中,Spring首先查看用户在web.xml
中有没有指定自定义的容器类,如果有,则通过Class.forName()
来获取该类的Class对象,如果没有指定,则使用默认的容器类,即WebXmlApplicationContext
实现。
至此整个Web应用的根上下文创建完成。
DispatcherServlet子容器的启动
在分析流程之前,首先来看一下该类的继承结构:
Spring在DispatcherServlet的设计实现中大量使用了模板模式,因此很多时候直接看子类跟本看不出什么所以然来,完整的处理逻辑中大都是在其父类中定义的,子类仅仅是重写了父类的某几个模板方法而已。
根上下文创建完成后, Spring会给每一个DispatcherServlet创建一个容器,其引用保存在其直接父类FrameworkServlet
中:
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
// ......
private WebApplicationContext webApplicationContext;
// ......
}
这个容器是根容器的子容器。
我们知道servlet容器(如Tomcat)在创建一个servlet时会首先调用init()方法来初始化servlet,因此应当首先查找该方法。
经过查找分析, 我们发现该容器的创建由父类HttpServletBean
的init()
方法触发,最后由子类FrameworkServlet
完成实际创建工作。
HttpServletBean#init()
方法大致结构如下:
// Set bean properties from init parameters.
try {
}
catch (BeansException ex) {
// ......
}
// 这是一个模板方法,由子类实现
initServletBean();
在try-catch
语句中没有创建容器的代码,略之,但是我们看到后面调用了initServletBean()
方法。但是HttpServletBean
的initServletBean()
方法仅仅是一个空实现,因此我们要去其子类FrameworkServlet#initServletBean()
中查看:
getServletContext().log("Initializing Spring FrameworkServlet ‘" + getServletName() + "‘");
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
// ......
}
终于,initXXXContext的字眼出现了 — initWebApplicationContext()
方法会首先从ServletContext
中获取到由ContextLoaderListener
初始化完成并放进入的根容器对象引用(因为创建子容器必须将父容器作为参数传递进去),然后经过层层调用,最终在createWebApplicationContext()
中完成了容器的创建工作,该方法的主要代码如下:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
// ......
// 通过工具类创建容器对象
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
如果想知道具体创建的到底是什么类型的容器,可以继续跟踪第一行中的getContextClass()
方法,最终可以发现也是XmlWebApplicationContext
。
DispatcherServlet请求处理流程
子容器创建完成后,当有请求到来时,DispatcherServlet就可以进行分发处理了。我们首先根据Servlet规范查找doService()
方法,这个方法在DispatcherServlet
本身就有实现:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ......try {
doDispatch(request, response);
}
finally {
// ......
}
我们省去了无关代码,于是可以明显看出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 {
// 查检是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 查找能够处理当前请求的Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 查找能够处理当前请求的Handler Adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ......
// 拦截器的preHandle()方法就是在这里被调用的
// 可见,如果preHandle()方法返回false,则终止分发流程,直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// 这一行是对我们写的Controller方法的真正调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
// 拦截器的postHandle()方法就是在这里被调用的,即在Controller调用完成之后
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// ......
}
finally {
// ......
}
}
分析到这里,DispatcherServlet的分发请求处理流程就一目了然了。
原文:http://blog.csdn.net/neosmith/article/details/44206671
内容总结
以上是互联网集市为您收集整理的Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程全部内容,希望文章能够帮你解决Spring源码分析: SpringMVC启动流程与DispatcherServlet请求处理流程所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。