基础

Web项目启动完毕后,就开始处理web请求了,那么DispatcherServlet就该出场了。
DispatcherServlet是实现Servlet接口的实现类。(菜鸟教程:Servlet 教程DispatcherServlet - 图1我们可以在Spring项目中增加自己的Servlet处理。
通常我们直接继承HttpServlet即可。 DispatcherServlet - 图2```java /**

  • 自定义Servlet *
  • @author lichlaughing */ public class MyServlet extends HttpServlet { private static final long serialVersionUID = 9211887734586730160L;

    @Override protected void doGet(HttpServletRequest req

    1. , HttpServletResponse resp) throws ServletException, IOException {
    2. resp.setContentType("text/html");
    3. getServletContext().getRequestDispatcher("/WEB-INF/views/servlet-index.jsp").forward(req, resp);

    } }

  1. 然后在web.xml中配置我们的servlet.
  2. ```xml
  3. <servlet>
  4. <servlet-name>myServlet</servlet-name>
  5. <servlet-class>cn.lichenghao.servlet.MyServlet</servlet-class>
  6. </servlet>
  7. <servlet-mapping>
  8. <servlet-name>myServlet</servlet-name>
  9. <url-pattern>/my-index</url-pattern>
  10. </servlet-mapping>

请求 http://localhost:8080/my-index 就能看到我们的 servlet-index.jsp 页面啦。

DispatcherServlet的操作和上面我们的MyServlet操作基本套路是一样的。
首先还是先看下其类的继承关系: DispatcherServlet - 图3

DispatcherServlet 的初始化

那么DispatcherServlet在什么时候初始化的?
web环境启动,会执行servlet的init方法。DispatcherServlet的init方法来到HttpServletBean。

  1. @Override
  2. public final void init() throws ServletException {
  3. // Set bean properties from init parameters.
  4. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  5. if (!pvs.isEmpty()) {
  6. try {
  7. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  8. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  9. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  10. initBeanWrapper(bw);
  11. bw.setPropertyValues(pvs, true);
  12. }
  13. catch (BeansException ex) {
  14. if (logger.isErrorEnabled()) {
  15. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  16. }
  17. throw ex;
  18. }
  19. }
  20. // Let subclasses do whatever initialization they like.
  21. initServletBean();
  22. }
  1. 拿到init-param中配置的参数;
  2. 将当前的bean转换成BeanWrapper,来进行属性的填充;
  3. 然后交给子类去init;

来到子类的初始化方法

  1. @Override
  2. protected final void initServletBean() throws ServletException {
  3. getServletContext().log("Initializing Spring " + getClass().getSimpleName()
  4. + " '" + getServletName() + "'");
  5. if (logger.isInfoEnabled()) {
  6. logger.info("Initializing Servlet '" + getServletName() + "'");
  7. }
  8. long startTime = System.currentTimeMillis();
  9. try {
  10. this.webApplicationContext = initWebApplicationContext();
  11. initFrameworkServlet();
  12. }
  13. catch (ServletException | RuntimeException ex) {
  14. logger.error("Context initialization failed", ex);
  15. throw ex;
  16. }
  17. if (logger.isDebugEnabled()) {
  18. String value = this.enableLoggingRequestDetails ?
  19. "shown which may lead to unsafe logging of potentially sensitive data" :
  20. "masked to prevent unsafe logging of potentially sensitive data";
  21. logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
  22. "': request parameters and headers will be " + value);
  23. }
  24. if (logger.isInfoEnabled()) {
  25. logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
  26. }
  27. }

这里主要有两个方法,initWebApplicationContext 初始化上下文,另一个是initFrameworkServlet 交给子类扩展。
所以来看:

  1. protected WebApplicationContext initWebApplicationContext() {
  2. WebApplicationContext rootContext
  3. = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  4. WebApplicationContext wac = null;
  5. if (this.webApplicationContext != null) {
  6. // A context instance was injected at construction time -> use it
  7. wac = this.webApplicationContext;
  8. if (wac instanceof ConfigurableWebApplicationContext) {
  9. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
  10. if (!cwac.isActive()) {
  11. // The context has not yet been refreshed -> provide services such as
  12. // setting the parent context, setting the application context id, etc
  13. if (cwac.getParent() == null) {
  14. // The context instance was injected without an explicit parent -> set
  15. // the root application context (if any; may be null) as the parent
  16. cwac.setParent(rootContext);
  17. }
  18. configureAndRefreshWebApplicationContext(cwac);
  19. }
  20. }
  21. }
  22. if (wac == null) {
  23. // No context instance was injected at construction time -> see if one
  24. // has been registered in the servlet context. If one exists, it is assumed
  25. // that the parent context (if any) has already been set and that the
  26. // user has performed any initialization such as setting the context id
  27. wac = findWebApplicationContext();
  28. }
  29. if (wac == null) {
  30. // No context instance is defined for this servlet -> create a local one
  31. wac = createWebApplicationContext(rootContext);
  32. }
  33. if (!this.refreshEventReceived) {
  34. // Either the context is not a ConfigurableApplicationContext with refresh
  35. // support or the context injected at construction time had already been
  36. // refreshed -> trigger initial onRefresh manually here.
  37. synchronized (this.onRefreshMonitor) {
  38. onRefresh(wac);
  39. }
  40. }
  41. if (this.publishContext) {
  42. // Publish the context as a servlet context attribute.
  43. String attrName = getServletContextAttributeName();
  44. getServletContext().setAttribute(attrName, wac);
  45. }
  46. return wac;
  47. }

到这里其实有点懵,因为这里也要initWebApplicationContext,在前面看到ContextLoader中也要initWebApplicationContext,这个重复工作?
确实这两个操作几乎是相同的,只不过他们关联了起来,变成了父子关系。

  • 首先他获取到了rootContext,其实就是ContextLoaderListener中创建的WebApplicationContext;
  • 紧接着创建自己的WebApplicationContext,并且把上面获取到的rootContext作为父上下文;

DispatcherServlet创建自己的WebApplicationContext。

  1. protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
  2. ...
  3. ConfigurableWebApplicationContext wac =
  4. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  5. wac.setEnvironment(getEnvironment());
  6. // 设置父上下文
  7. wac.setParent(parent);
  8. ...
  9. return wac;
  10. }

好了到这里一个基本的DispatcherServlet对象就创建出来了,但是还是个雏形,因为http能处理各种各样的请求,那就需要请求映射器,还能使用各种各样的渲染引擎,还能上传下载文件等等,这些东西好像都还没有安排上,那么在创建完基本对象后,来补充这些能力。

回到 initWebApplicationContext 方法中,当创建完毕后,有这样一块代码:

  1. if (!this.refreshEventReceived) {
  2. // Either the context is not a ConfigurableApplicationContext with refresh
  3. // support or the context injected at construction time had already been
  4. // refreshed -> trigger initial onRefresh manually here.
  5. synchronized (this.onRefreshMonitor) {
  6. onRefresh(wac);
  7. }
  8. }

跟进onRefresh方法就会发现:

  1. @Override
  2. protected void onRefresh(ApplicationContext context) {
  3. initStrategies(context);
  4. }
  5. /**
  6. * Initialize the strategy objects that this servlet uses.
  7. * <p>May be overridden in subclasses in order to initialize further strategy objects.
  8. */
  9. protected void initStrategies(ApplicationContext context) {
  10. initMultipartResolver(context);
  11. initLocaleResolver(context);
  12. initThemeResolver(context);
  13. initHandlerMappings(context);
  14. initHandlerAdapters(context);
  15. initHandlerExceptionResolvers(context);
  16. initRequestToViewNameTranslator(context);
  17. initViewResolvers(context);
  18. initFlashMapManager(context);
  19. }

这块逻辑正是我们上面说的,补充各种各样的能力的地方。
这块等下再说,通过变量可以猜测出refreshEventReceived这是个事件监听的操作,如果没有监听到这个事件那么就刷新操作。
那也就意味着有地方有事件监听器。如下所示:
FrameworkServlet的内部类,实现了监听器。

  1. private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
  2. @Override
  3. public void onApplicationEvent(ContextRefreshedEvent event) {
  4. FrameworkServlet.this.onApplicationEvent(event);
  5. }
  6. }

这就明确了,监听到事件就执行刷新方法,同时变量refreshEventReceived更新为true,那么就不用手动触发。否则就手动触发。

initHandlerMappings

来看处理器映射器是如何初始化的,这个可以看成是请求url和对应Controller的映射。
DispatcherServlet接收到请求后交给handlermapping,它去匹配到对应的Controller给DispatcherServlet。

  1. private void initHandlerMappings(ApplicationContext context) {
  2. this.handlerMappings = null;
  3. if (this.detectAllHandlerMappings) {
  4. // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
  5. Map<String, HandlerMapping> matchingBeans =
  6. BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
  7. if (!matchingBeans.isEmpty()) {
  8. this.handlerMappings = new ArrayList<>(matchingBeans.values());
  9. // We keep HandlerMappings in sorted order.
  10. AnnotationAwareOrderComparator.sort(this.handlerMappings);
  11. }
  12. } else {
  13. try {
  14. HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
  15. this.handlerMappings = Collections.singletonList(hm);
  16. } catch (NoSuchBeanDefinitionException ex) {
  17. // Ignore, we'll add a default HandlerMapping later.
  18. }
  19. }
  20. // Ensure we have at least one HandlerMapping, by registering
  21. // a default HandlerMapping if no other mappings are found.
  22. if (this.handlerMappings == null) {
  23. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
  24. if (logger.isTraceEnabled()) {
  25. logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
  26. "': using default strategies from DispatcherServlet.properties");
  27. }
  28. }
  29. }

默认情况下会加载Applicontext下所有实现HandlerMapping接口的bean(会有多个),否则只加载名称为handlerMapping的bean(只有一个)。
如果依然没有找到HandlerMapping那么就根据默认策略实例化一个默认的。

  1. <init-pararn>
  2. <param-name>detectAllHandlerMappings</pararm-name>
  3. <param-value>false</param-value> <!-- 只加载名称为handlerMapping的bean -->
  4. </init-pararm>

这个默认策略是在静态块中初始化的,加载了一个配置文件 DispatcherServlet.properties .

  1. static {
  2. // Load default strategy implementations from properties file.
  3. // This is currently strictly internal and not meant to be customized
  4. // by application developers.
  5. try {
  6. ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
  7. defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
  8. } catch (IOException ex) {
  9. throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
  10. }
  11. }

配置文件

  1. # Default implementation classes for DispatcherServlet's strategy interfaces.
  2. # Used as fallback when no matching beans are found in the DispatcherServlet context.
  3. # Not meant to be customized by application developers.
  4. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
  5. org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
  6. org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
  7. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
  8. org.springframework.web.servlet.function.support.RouterFunctionMapping
  9. org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
  10. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
  11. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
  12. org.springframework.web.servlet.function.support.HandlerFunctionAdapter
  13. org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
  14. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
  15. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
  16. org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
  17. org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
  18. org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

这里有个bean需要关注下:RequestMappingHandlerMapping 在controller和方法上的注解@RequestMapping就是由它处理的。 DispatcherServlet - 图4

initHandlerAdapters

初始化请求适配器。初始化逻辑和initHandlerMappings基本是一样的。同样可以使用参数detectAllHandlerAdapters来配置是否加载所有。

  1. <init-param>
  2. <param-name>detectAllHandlerAdapters</param-name>
  3. <param-value>false</param-value>
  4. </init-param>

同样如果没有匹配到,那么系统会根据默认策略生成默认的处理器适配器。

DispatcherServlet 的请求处理

XX