从上一篇文章中Spring MVC原理图中我们可以看出:DispatcherServlet 在 Spring MVC框架 中处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应的工作。在分析 DispatcherServlet 源码之前,我们先来看一下请求处理的大致流程:
DispatcherServlet 继承自 HttpServlet,它遵循 Servlet 里的“init-service-destroy”三个阶段,首先我们先来看一下它的 init() 阶段。
首先我们来看一下DispatcherServlet的继承关系,它继承自FrameworkServlet,而FrameworkServlet又继承自HttpServletBean。
DispatcherServlet 的 init() 方法在其父类 HttpServletBean 中实现的,它覆盖了 GenericServlet 的 init() 方法,主要作用是加载 web.xml 中 DispatcherServlet 的
/\*\*
\* Map config parameters onto bean properties of this servlet, and
\* invoke subclass initialization.
\* @throws ServletException if bean properties are invalid (or required
\* properties are missing), or if subclass initialization fails.
\*/
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.(ServletConfigPropertyValues 是静态内部类,它使用 ServletConfig 获取 web.xml 中配置的参数)
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 使用 BeanWrapper 来构造 DispatcherServlet
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;
}
}
// Let subclasses do whatever initialization they like.(让子类实现的方法,这种在父类定义在子类实现的方式叫做模版方法模式)
initServletBean();
}
如上所述,web.xml 中配置的参数是 ServletConfigPropertyValues 使用 ServletConfig 获取到的,ServletConfigPropertyValues 的代码如下所示:
/**
* PropertyValues implementation created from ServletConfig init parameters.
*/
private static class ServletConfigPropertyValues extends MutablePropertyValues {
/\*\*
\* Create new ServletConfigPropertyValues.
\* @param config the ServletConfig we'll use to take PropertyValues from
\* @param requiredProperties set of property names we need, where
\* we can't accept default values
\* @throws ServletException if any required properties are missing
\*/
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException {
Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<>(requiredProperties) : null);
Enumeration<String> paramNames = config.getInitParameterNames();
while (paramNames.hasMoreElements()) {
String property = paramNames.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
// Fail if we are still missing properties.
if (!CollectionUtils.isEmpty(missingProps)) {
throw new ServletException(
"Initialization from ServletConfig for servlet '" + config.getServletName() +
"' failed; the following required properties were missing: " +
StringUtils.collectionToDelimitedString(missingProps, ", "));
}
}
}
在 HttpServletBean 的 init() 方法中调用了 initServletBean() 这个方法,它是在 FrameworkServlet 类中实现的,主要作用是建立 WebApplicationContext 容器(有时也称上下文),并加载 SpringMVC 配置文件中定义的 Bean 到改容器中,最后将该容器添加到 ServletContext 中。下面是 initServletBean() 方法的具体代码:
/\*\*
\* 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 " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 初始化 WebApplicationContext (即SpringMVC的IOC容器)
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
WebApplicationContext 继承于 ApplicationContext 接口,从容器中可以获取当前应用程序环境信息,它也是 SpringMVC 的 IOC 容器。下面是 initWebApplicationContext() 方法的具体代码:
/\*\*
\* 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() {
// 获取 ContextLoaderListener 初始化并注册在 ServletContext 中的根容器,即 Spring 的容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it(因为 WebApplicationContext 不为空,说明该类在构造时已经将其注入,可以直接使用它)
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(将 Spring 的容器设为 SpringMVC 容器的父容器)
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(如果 WebApplicationContext 为空,则进行查找,能找到说明上下文已经在别处初始化。)
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one(如果 WebApplicationContext 仍为空,则以 Spring 的容器为父上下文建立一个新的。)
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.
synchronized (this.onRefreshMonitor) {
// 模版方法,由 DispatcherServlet 实现
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.(发布这个 WebApplicationContext 容器到 ServletContext 中)
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
下面是查找 WebApplicationContext 的 findWebApplicationContext() 方法代码:
/\*\*
\* Retrieve a {@code WebApplicationContext} from the {@code ServletContext}
\* attribute with the {@link #setContextAttribute configured name}. The
\* {@code WebApplicationContext} must have already been loaded and stored in the
\* {@code ServletContext} before this servlet gets initialized (or invoked).
\* <p>Subclasses may override this method to provide a different
\* {@code WebApplicationContext} retrieval strategy.
\* @return the WebApplicationContext for this servlet, or {@code null} if not found
\* @see #getContextAttribute()
\*/
@Nullable
protected WebApplicationContext findWebApplicationContext() {
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;
}
建立好 WebApplicationContext(上下文) 后,通过 onRefresh(ApplicationContext context) 方法回调,进入 DispatcherServlet 类中。onRefresh() 方法,提供 SpringMVC 的初始化,具体代码如下:
/\*\*
\* 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) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
在 initStrategies() 方法中进行了各个组件的初始化,先来看一下这些组件的初始化方法,稍后再来详细分析这些组件。
initHandlerMappings() 方法从 SpringMVC 的容器及 Spring 的容器中查找所有的 HandlerMapping 实例,并把它们放入到 handlerMappings 这个 list 中。这个方法并不是对 HandlerMapping 实例的创建,HandlerMapping 实例是在上面 WebApplicationContext 容器初始化,即 SpringMVC 容器初始化的时候创建的。
/\*\*
\* Initialize the HandlerMappings used by this class.
\* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
\* we default to BeanNameUrlHandlerMapping.
\*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.(从 SpringMVC 的 IOC 容器及 Spring 的 IOC 容器中查找 HandlerMapping 实例)
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.(按一定顺序放置 HandlerMapping 对象)
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER\_MAPPING\_BEAN\_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.(如果没有找到 HandlerMapping,则加载默认的)
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/\*\*
\* Initialize the HandlerAdapters used by this class.
\* <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
\* we default to SimpleControllerHandlerAdapter.
\*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.(从 SpringMVC 的 IOC 容器及 Spring 的 IOC 容器中查找 HandlerAdapters 实例)
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.(按一定顺序放置 HandlerAdapters 对象)
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER\_ADAPTER\_BEAN\_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.(如果没有找到 HandlerAdapters,则加载默认的)
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
HttpServlet 提供了 doGet()、doPost() 等方法,DispatcherServlet 中这些方法是在其父类 FrameworkServlet 中实现的,代码如下:
/\*\*
\* Delegate GET requests to processRequest/doService.
\* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
\* with a {@code NoBodyResponse} that just captures the content length.
\* @see #doService
\* @see #doHead
\*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/\*\*
\* Delegate POST requests to {@link #processRequest}.
\* @see #doService
\*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
这些方法又都调用了 processRequest() 方法,我们来看一下它的代码:
/\*\*
\* Process this request, publishing an event regardless of the outcome.
\* <p>The actual event handling is performed by the abstract
\* {@link #doService} template method.
\*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 返回与当前线程相关联的 LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 根据请求构建 LocaleContext,公开请求的语言环境为当前语言环境
LocaleContext localeContext = buildLocaleContext(request);
// 返回当前绑定到线程的 RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 根据请求构建ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 获取当前请求的 WebAsyncManager,如果没有找到则创建
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 使 LocaleContext 和 requestAttributes 关联
initContextHolders(request, localeContext, requestAttributes);
try {
// 由 DispatcherServlet 实现
doService(request, response);
}
catch (ServletException | 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();
}
logResult(request, response, failureCause, asyncManager);
// 发布 ServletRequestHandlerEvent 事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
DispatcherServlet 的 doService() 方法主要是设置一些 request 属性,并调用 doDispatch() 方法进行请求分发处理,doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView ,ModelAndView 是连接“业务逻辑层”与“视图展示层”的桥梁,接下来就要通过 ModelAndView 获得 View,再通过它的 Model 对 View 进行渲染。doDispatch() 方法如下:
/\*\*
\* Process the actual dispatching to the handler.
\* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
\* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
\* to find the first that supports the handler class.
\* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
\* themselves to decide which methods are acceptable.
\* @param request current HTTP request
\* @param response current HTTP response
\* @throws Exception in case of any kind of processing failure
\*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 获取当前请求的WebAsyncManager,如果没找到则创建并与请求关联
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是否有 Multipart,有则将请求转换为 Multipart 请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.(遍历所有的 HandlerMapping 找到与请求对应的 Handler,并将其与一堆拦截器封装到 HandlerExecution 对象中。)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.(遍历所有的 HandlerAdapter,找到可以处理该 Handler 的 HandlerAdapter)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 遍历拦截器,执行它们的 preHandle() 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.(执行实际的处理程序)
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;
}
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) {
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) {
// 遍历拦截器,执行它们的 afterCompletion() 方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章