1. DispatcherServlet初始化流程
我们已知道Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。下面是DispatcherServlet的类结构图:
DispatcherServlet是一个Servlet且继承了HttpServlet,它必然有Servlet的生命周期“init-service-destroy”,下面我们便从init来展开分析。
1.1 HttpServletBean#init()源码解析
DispatcherServlet的init()方法是在HttpServletBean中实现的,HttpServletBean重写了GenericServlet的init()方法,主要作用是加载 web.xml 中 DispatcherServlet 的 <init-param> 配置,并调用子类的初始化,源码如下:
@Override
public final void init() throws ServletException {
//ServletConfigPropertyValues是静态内部类,它可以通过requiredProperties参数校验哪些属性是必要的,将ServletConfig中的初始化参数都包装成PropertyValue对象放入List集合中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
//使用 BeanWrapper 来构造 DispatcherServlet,设置DispatcherServlet属性
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");
}
}
/**
* config表示web.xml的属性配置
* requiredProperties表示的是必须有的属性
*/
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException {
//如果requiredProperties不为空则重新创建一个Set集合
Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<String>(requiredProperties) : null);
//获取web.xml中的所有初始化参数的参数名枚举
Enumeration<String> paramNames = config.getInitParameterNames();
//将所有的初始化参数包装成PropertyValue对象并放入List集合中
while (paramNames.hasMoreElements()) {
String property = paramNames.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
//如果缺少了必须的属性则抛出ServletException异常
if (!CollectionUtils.isEmpty(missingProps)) {
throw new ServletException(
"Initialization from ServletConfig for servlet '" + config.getServletName() +
"' failed; the following required properties were missing: " +
StringUtils.collectionToDelimitedString(missingProps, ", "));
}
}
1.2 FramworkServelt#initServletBean源码解析
initServletBean()方法是在FramworkServelt中实现,用于初始化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 {
//初始化WebApplicationContext容器
this.webApplicationContext = initWebApplicationContext();
//暂时没有具体的实现,可以留给开发人员在子类中实现一些特有的属性
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (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");
}
}
/**
* Initialize and publish the WebApplicationContext for this servlet.
* <p>Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
*/
protected WebApplicationContext initWebApplicationContext() {
//从ServletContext中获取根的Context对象,如使用Spring则是Spring的IOC容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 如果当前的WebApplicationContext在构造时已经被注入则使用它
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
//如果context对象是ConfigurableWebApplicationContext
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
//如果ConfigurableWebApplicationContext 不是存活状态
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);
}
//配置并刷新WebApplicationContext
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果在该实例构造时context没有被注入,则查询是否已经注册在ServletContext中
wac = findWebApplicationContext();
}
if (wac == null) {
// 在ServletContext没有context实例被定义则通过rootContext创建一个
wac = createWebApplicationContext(rootContext);
}
//refreshEventReceived默认为false
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);
}
// publishContext默认为true,允许把当前容器作为ServeltContext的一个属性
if (this.publishContext) {
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;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//判断应用上下文唯一的id,Class+HashCode
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
//基于可用的信息配置一个更有用的id
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
//设置默认的id值,如“org.springframework.web.context.WebApplicationContext:/dispatcher”
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
//针对ContextRefreshListener事件监听
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
//当上下文被刷新时调用上下文环境的initPropertySources方法
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//在刷新和激活作为这个servlet的上下文之前,对给定的WebApplicationContext进行后置处理,这个方法默认实现为空,可以由开发人员自定义
postProcessWebApplicationContext(wac);
// spring dispatcherServlet初始化的时候可以指定初始化一些类
applyInitializers(wac);
wac.refresh();
}
protected WebApplicationContext findWebApplicationContext() {
//获取在ServletContext中的属性名
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
//通过属性名查找ServletContext中的WebApplicationContext
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//获取contextClass,默认值是XmlWebApplicationContext.class
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
//判断ConfigurableWebApplicationContext是否与contextClass相同或是它的父类或接口
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//根据Class类型创建实例
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
//配置并刷新WebApplicationContext
configureAndRefreshWebApplicationContext(wac);
return wac;
}
2. DispatcherServlet处理流程
2.1 源码分析
我们知道请求一个Servlet会调用其service()方法,查看源码发现FrameworkServlet重写了HttpServlet的service()方法,源码如下:
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取http请求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
//如果Http请求方法是PATCH类型或为空
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
processRequest(request, response);
}
else {
//调用HttpServlet的service方法
super.service(request, response);
}
}
/**
* HttpServlet#service方法
* @desc 由于HttpServlet中doHead方法中调用doGet来处理请求,因此FrameworkServlet重写了处理
* GET, POST, PUT, DELETE, OPTIONS, TRACE请求的方法,这些方法都做相关处理后委托给processRequest方法来处理
*/
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
/**
* 集中处理Http各种类型的请求
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//封装当前请求的Local信息
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
//封装当前请求的request,response信息,方法操作
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//异步请求的相关管理
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将LocaleContext、RequestAttributes 分别封装进 LocaleContextHolder、RequestContextHolder
//LocaleContextHolder、RequestContextHolder 这两个抽象类其实都是利用 ThreadLocal 将信息存放在线程中
initContextHolders(request, localeContext, requestAttributes);
try {
//DispatcherServlet中实现
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
//释放LocaleContext、RequestAttributes
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
//发布请求处理完毕事件,包括获取状态码和发布事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
DispatcherServlet的doService()方法设置各种request属性后调用doDispatch()方法利用各种处理器来处理请求,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);
// 通过HandlerMapping获取HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 通过handler获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
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;
}
}
// HandlerExecutionChain调用所有拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 通过HandlerAdapter获取ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果ModelAndView非空且view为空使用默认ViewName
applyDefaultViewName(processedRequest, mv);
// HandlerExecutionChain调用所有拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理ModelAndView和Exception
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//调用所有拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
/**
* 处理ModelAndView
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//异常处理部分,获取error视图
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 处理程序是否返回一个视图来渲染?
if (mv != null && !mv.wasCleared()) {
// 获取视图和渲染视图
render(mv, request, response);
// 视图是error视图,则移除Request中Error属性
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// 依次调用所有拦截器的 afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 确定请求的区域设置并将其应用于响应
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
// 判断ModelAndView的view属性是否是String类型,如果是String类型使用视图解析器解析
if (mv.isReference()) {
// 解析视图名
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
// 设置响应状态
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// View对象根据ModelAndView渲染视图
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
/**
* 解析视图名称
*/
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
// 遍历视图解析器并解析视图
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
关于HanlderMapping和HandlerAdapter的详解见SpringMVC源码解析之HandlerMapping与HandlerAdapter,关于视图解析方面的详解见SpringMVC源码解析之ViewResolver。
2.2 流程总结
请求执行流程图如下:
总结SpringMVC的主要执行流程如下:
- Tomcat 启动,对 DispatcherServlet 进行实例化,然后调用它的 init() 方法进行初始化,在这个初始化过程中完成了:对 web.xml 中初始化参数的加载;建立 WebApplicationContext (SpringMVC的IOC容器);进行组件的初始化;
- 客户端发出请求,由 Tomcat 接收到这个请求,如果匹配 DispatcherServlet 在 web.xml 中配置的映射路径,Tomcat 就将请求转交给 DispatcherServlet 处理;
- DispatcherServlet 从容器中取出所有 HandlerMapping 实例(每个实例对应一个 HandlerMapping 接口的实现类)并遍历,每个 HandlerMapping 会根据请求信息,通过自己实现类中的方式去找到处理该请求的 Handler (执行程序,如Controller中的方法),并且将这个 Handler 与一堆 HandlerInterceptor (拦截器) 封装成一个 HandlerExecutionChain 对象,一旦有一个 HandlerMapping 可以找到 Handler 则退出循环;
- DispatcherServlet 取出 HandlerAdapter 组件,根据已经找到的 Handler,再从所有 HandlerAdapter 中找到可以处理该 Handler 的 HandlerAdapter 对象;
- 执行 HandlerExecutionChain 中所有拦截器的 preHandler() 方法,然后再利用 HandlerAdapter 执行 Handler ,执行完成得到 ModelAndView,再依次调用拦截器的 postHandler() 方法;
- 利用 ViewResolver 将 ModelAndView 或是 Exception(可解析成 ModelAndView)解析成 View,然后 View 会调用 render() 方法再根据 ModelAndView 中的数据渲染出页面;
- 最后再依次调用拦截器的 afterCompletion() 方法,这一次请求就结束了。
………