工作流程
加载 DispatcherServlet
追朔源码,最终晓得这个也和Servlet有了瓜葛,要想完成用户的一次请求,第一步就是加载 DispatcherServlet,下面我们介绍一下过程:
class DispatcherServlet extends FrameworkServlet … ——>
class FrameworkServlet extends HttpServletBean … ——>
class HttpServletBean extends HttpServlet … ——>
class HttpServlet extends GenericServlet … ——>
abstract class GenericServlet implements Servlet …
分析:
我们从下向上看,可以知道Servlet是一个接口,那Servlet到底是什么东西呢,说白了就是开发动态网站的技术,Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
* 编写一个Java类,实现servlet接口。
* 把开发好的Java类部署到web服务器中。
是不是明白点了,其实在设计什么东西的时候往往都会留下一些疑问,因为什么东西都不可能一次性设计好,比如一个好的程序更是常常在更新,所以Sun公司也就提供了两个类实现Servlet接口,是HttpServlet和GenericServlet,其中
HttpServlet
指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。而
GenericServlet
这个类的存在使得编写Servlet更加方便。它提供了一个简单的方案,这个方案用来执行有关Servlet生命周期的方法以及在初始化时对ServletConfig对象和ServletContext对象进行说明。
DispatcherServlet 的init()方法(初始化方法)在其父类HttpServletBean
中实现,主要作用是加载web.xml(总配置文件)中DispatcherServlet 的配置,并调用子类的初始化。
* init() @Override public final void init() throws ServletException { // Set
bean properties from init parameters. 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; } } // Let subclasses
do whatever initialization they like. initServletBean(); }
在 HttpServletBean 的init() 方法中调用了 initServletBean() 方法,这个方法在 FrameworkServlet
类中实现,主要作用是建立 WebApplicationContext 容器。
* initServletBean()源码 @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 { 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
initWebApplicationContext()方法主要用于创建和刷新WebApplicationContext实例,并对Servlet功能所使用的变量进行初始化。
initWebApplicationContext()源码 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. synchronized (this.onRefreshMonitor) { onRefresh(wac);
} } if (this.publishContext) { // Publish the context as a servlet context
attribute. String attrName = getServletContextAttributeName(); getServletContext
().setAttribute(attrName, wac); } return wac; }
* 建立好 WebApplicationContext 后,通过onRefresh(ApplicationContext context) 方法回调,进入
DispatcherServlet 类中,因为在DispatcherServlet 类中存在 onRefresh() 方法。
onRefresh()
@Override protected void onRefresh(ApplicationContext context) { initStrategies
(context); }
initStrategies() 负责SpringMVC九个组件的初始化。
initStrategies()
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(
context); initHandlerMappings(context); initHandlerAdapters(context);
initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context)
; initViewResolvers(context); initFlashMapManager(context); }
走到这里就算完成了第一步了。
如果没有配置HandlerMapping,HandlerAdapter,ViewResolver等组件就会使用
DispatcherServlet.properties 文件中默认的组件。
DispatcherServlet.properties文件内容如下
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet
context. # Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
紧接着就是 DispatcherServlet 调用 HandlerMapping 来处理 request(请求) 和 handler(处理)
之间的映射关系,调用 HandlerMapping 类的唯一方法 getHandler() ,而 handler 被包装化为
HandlerExecutionChain类 ,也就是 HandlerMapping 直接与 HandlerExecutionChain 打交道,
HandlerMapping 的 getHandler() 源码如下:
@Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws
Exception;
可以得知,的确是与 HandlerExecutionChain 直接打交道,调用了其 getHandler方法,而且方法参数也是 request
,这也就对应上了 HandlerMapping 的确是处理 request 和 handler 之间的映射关系,
HandlerExecutionChain 的 getHandler() 源码如下:
public Object getHandler() { return this.handler; }
这下就得到了真正的handler(所谓的处理器对象)。
HandlerExecutionChain 这个类也可以说是 handler执行链,包含了 handler 和 interceptor ,handler 由
getHandler()方法可以得到,interceptor 由 getInterceptors() 方法可以得到,所以配置文件也可能会加入这里。
得到的 handler 会根据url,method,context-type等找到对应的控制器(controller),然后调用
getHandlerAdapter() 方法得到 HandlerAdapter,
getHandlerAdapter()源码
protected HandlerAdapter getHandlerAdapter(Object handler) throws
ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter
adapter: this.handlerAdapters) { if (adapter.supports(handler)) { return adapter
; } } } throw new ServletException("No adapter for handler [" + handler + "]:
The DispatcherServlet configuration needs to include a HandlerAdapter that
supports this handler"); }
在HandlerAdapter 中首先调用 supports() 方法判断是否支持该 handler ,如果支持则经过适配执行具体的 Controller
,Controller 将请求处理的具体结果放入 ModelAndView 中,handler 将结果传给 ViewResolver
渲染解析得到最终的结果反还给用户。