工作流程

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

技术
下载桌面版
GitHub
Microsoft Store
SourceForge
夸克网盘
百度网盘
云服务器优惠
华为云优惠券
京东云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信