这里主要是对Spring下DispatcherServlet处理http请求的流程做一个简单梳理!
DispatcherServlet
首先,我们先了解下原生java servlet处理http请求流程,大体上如下,
- 当客户端向服务器端发起http请求
- 请求会到达客户端服务器,如tomcat
- 服务器会根据请求路径匹配到对应的servtlet全限定名(xml文件中)
- 服务器会通过反射的方式实例化这个servlet(只会在第一次请求时实例化),并创建Servletrequest和ServletResponse两个对象,ServerletRequest用来封装请求对象,ServerletResponse用来封装处理结果,主要请求流程在service(ServerletRequest,ServerletResponse)中
以上具体可以参考 转自
那么,在Spring中,http请求处理流程又是怎么样的呢?
同样的,也是Serverlet,是DispatcherServerlet,两者关系如下图
上图可以看出,DispatcherServlet是Servlet的唯一实现子类,并且被Spring容器管理,所以可以看出spring中是通过DispatcherServlet来处理Http请求,下面我们详细探讨下这个处理流程
整个流程可以分为两个阶段,初始化和处理请求
- 初始化: 请求到达服务器初始化,初始化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.
//注入servelet.xml的配置属性到bean中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 这里是实例化对象的核心方法
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
在看这个initServletBean()方法,其中initWebApplicationContext()为核心方法,用来创建DispatcherServlet的上下文对象
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
initWebApplicationContext()的代码如下,先获得ContextLoaderListener创建的根应用上下文,为DispatcherServlet创建自己的应用上下文,再刷新上下文对象
/**
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
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);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
onRefresh(wac),这个是刷新上下文对象的方法,初始化一些核心对象
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* 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,该Bean用于文件上传
initMultipartResolver(context);
//初始化LocaleResolver实例,这是国际化资源对象,例如我在中国,在访问服务器时,浏览器中设置的本地化类型会包含在HTML请求中,
//发送给WEB服务器,如请求报文中的Accept-Language参数再比如我们看见某些网站上有英文.中文.繁体切换等按钮,这些可以
//通过cookie/session/请求参数来切换本地化类型
initLocaleResolver(context);
//初始化图形解析器Bean,前后端分离的模式下用的相对较少了
initThemeResolver(context);
//当detectAllHandlerMappings为true时,从容器(以及祖先容器)获取所有类型为HandlerMapping的bean组件,记录到handlerMappings并排序;
//当detectAllHandlerMappings为false时,从容器(以及祖先容器)获取名称为handlerMapping的bean组件,记录到handlerMappings,这种情况下handlerMappings中最多有一个元素;
initHandlerMappings(context);
//这个initHandlerMappings类似
initHandlerAdapters(context);
//当detectAllHandlerExceptionResolvers为true时,从容器(以及祖先容器)获取所有类型为HandlerExceptionResolver的bean组件,记录到handlerExceptionResolvers并排序;
//当detectAllHandlerExceptionResolvers为false时,从容器(以及祖先容器)获取名称为handlerExceptionResolver的bean组件,记录到handlerExceptionResolvers,这种情况下handlerExceptionResolvers中最多有一个元素;
initHandlerExceptionResolvers(context);
//初始化RequestToViewNameTranslator实例对象.默认实现是DefaultRequestToViewNameTranslator
initRequestToViewNameTranslator(context);
//初始化ViewResolver实例,同initHandlerMappings(),获取所有ViewResolver类型的实例放在容器中
initViewResolvers(context);
//初始化FlashMapManager对象,默认实现是SessionFlashMapManager
initFlashMapManager(context);
}
以上是DispatcherServlet的基本初始化的流程,下面我们来看下DispatcherServlet处理http请求的一个流程