工作流程
加载 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 渲染解析得到最终的结果反还给用户。