SpringMVC的DispatcherServlet如何加入tomcat的servlet提供服务请求

393 阅读3分钟

之前看了网上很多文章,了解DispatcherServlet是SpringMVC的实现核心,那它是如何作为核心的servlet做做统一的请求分处理,这个servlet是如何加入到tomcat中,作为核心的请求处理的。

SpringBoot的加载DispatcherServlet的配置类

DispatcherServletAutoConfiguration是SpringBoot框架自动加载的DispatcherServlet到Spring Beanfactory的配置

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

   /*
    * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
    */
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

   /*
    * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
    */
   public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {

   @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
   public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
      DispatcherServlet dispatcherServlet = new DispatcherServlet();
      dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
      dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
      dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
      dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
      dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
      return dispatcherServlet;
   }

   @Bean
   @ConditionalOnBean(MultipartResolver.class)
   @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
   public MultipartResolver multipartResolver(MultipartResolver resolver) {
      // Detect if the user has created a MultipartResolver but named it incorrectly
      return resolver;
   }

}

DispatcherServletRegistrationConfiguration的bean的注入到Spring的BeanFactory容器

@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {

   @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
   @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
   public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
         WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
      DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
            webMvcProperties.getServlet().getPath());
      registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
      registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
      multipartConfig.ifAvailable(registration::setMultipartConfig);
      return registration;
   }

}

由于DispatcherServletRegistrationBean是继承自ServletContextInitializer的接口

public abstract class RegistrationBean implements ServletContextInitializer, Ordered {

   private static final Log logger = LogFactory.getLog(RegistrationBean.class);

   private int order = Ordered.LOWEST_PRECEDENCE;

   private boolean enabled = true;

   @Override
   public final void onStartup(ServletContext servletContext) throws ServletException {
      String description = getDescription();
      if (!isEnabled()) {
         logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
         return;
      }
      register(description, servletContext);
   }

下面是TomcatServletWebServerFactory初始化Tomcat的配置Context的函数,这里TomcatStart也是实现ServletContainerInitializer接口

  1. 创建TomcatStarter对象,并且判断设置到TomcatEmbeddedContext对象中。
  2. 增加ServletContainerInitializer到TomcatEmbeddedContext对象中,这里其实就是将所有的ServletContainerInitializer初始化实现类都代理到TomcatStarter中,再由TomcatEmbeddedContext启动时候去执行其onStartup方法。
 */
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
   TomcatStarter starter = new TomcatStarter(initializers);
   if (context instanceof TomcatEmbeddedContext) {
      TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
      embeddedContext.setStarter(starter);
      embeddedContext.setFailCtxIfServletStartFails(true);
   }
   context.addServletContainerInitializer(starter, NO_CLASSES);
   for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
      context.addLifecycleListener(lifecycleListener);
   }
}

由于ServletContextInitializer接口是Tomcat的TomcatEmbeddedContext的父类StandardContext的startIntenal启动函数中执行其持有的initializers集合(即实现ServletContainerInitializer接口初始化类),并执行其OnStartup方法,

// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
    initializers.entrySet()) {
    try {
        entry.getKey().onStartup(entry.getValue(),
                getServletContext());
    } catch (ServletException e) {
        log.error(sm.getString("standardContext.sciFail"), e);
        ok = false;
        break;
    }
}

现在回过来看DispatcherServletRegistrationBean的父类DynamicRegistrationBean的register函数。

@Override
protected final void register(String description, ServletContext servletContext) {
   D registration = addRegistration(description, servletContext);
   if (registration == null) {
      logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
      return;
   }
   configure(registration);
}

ServletRegistrationBean类实现DynamicRegistrationBean的addRegistration方法,

  1. 获取Servlet的名称,就是dispatcherServlet。
  2. 注册DespatcherServlet的bean到ServletContext中。
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
   String name = getServletName();
   return servletContext.addServlet(name, this.servlet);
}

从DispatcherServletRegistrationBean的构造函数,将DispatcherServlet传到父类中,父类就是ServletRegistrationBean

public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) { 
  //调用父类的ServletRegistrationBean的构造方法,传入DispatcherServlet的bean
   super(servlet);
   Assert.notNull(path, "Path must not be null");
   this.path = path;
   super.addUrlMappings(getServletUrlMapping());
}

DispatcherServletRegistrationBean的父类ServletRegistrationBean构造方法。

public ServletRegistrationBean(T servlet, String... urlMappings) {
   this(servlet, true, urlMappings);
}

public ServletRegistrationBean(T servlet, boolean alwaysMapUrl, String... urlMappings) {
   Assert.notNull(servlet, "Servlet must not be null");
   Assert.notNull(urlMappings, "UrlMappings must not be null");
   this.servlet = servlet;
   this.alwaysMapUrl = alwaysMapUrl;
   this.urlMappings.addAll(Arrays.asList(urlMappings));
}

至此,tomcat启动完后,就完成了ServletContext的添加DispatcherServlet这个Servlet. 而Tomcat则是通过StandardWrapperValve的invoke方法中去构造ApplicationFilterChain,然后最后调用Servlet(即DispatcherServlet)的service方法完成请求处理,接下来就是到SpringMVC去调用HandleMapping,再调用HandlerMappingAdapter去调用对应的Controller了,

private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {

    // Call the next filter if there is one
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            Filter filter = filterConfig.getFilter();

            if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                    filterConfig.getFilterDef().getAsyncSupported())) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
            }
            if( Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();

                Object[] args = new Object[]{req, res, this};
                SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
            } else {
                filter.doFilter(request, response, this);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.filter"), e);
        }
        return;
    }

    // We fell off the end of the chain -- call the servlet instance
    try {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(request);
            lastServicedResponse.set(response);
        }

        if (request.isAsyncSupported() && !servletSupportsAsync) {
            request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                    Boolean.FALSE);
        }
        // Use potentially wrapped request from this point
        if ((request instanceof HttpServletRequest) &&
                (response instanceof HttpServletResponse) &&
                Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            Principal principal =
                ((HttpServletRequest) req).getUserPrincipal();
            Object[] args = new Object[]{req, res};
            SecurityUtil.doAsPrivilege("service",
                                       servlet,
                                       classTypeUsedInService,
                                       args,
                                       principal);
        } else {
           // 就是DispatcherServlet的service方法
            servlet.service(request, response);
        }
    } catch (IOException | ServletException | RuntimeException e) {
        throw e;
    } catch (Throwable e) {
        e = ExceptionUtils.unwrapInvocationTargetException(e);
        ExceptionUtils.handleThrowable(e);
        throw new ServletException(sm.getString("filterChain.servlet"), e);
    } finally {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(null);
            lastServicedResponse.set(null);
        }
    }

总结
本文主要对SprintBoot对SpringMvc中DispatcherServlet是如何加入Tomcat成为Servlet的过程,并成为Servlet请求处理入口的整合的分析,欢迎一起交流