SpringMVC源码分析之Servlet容器启动和Servlet组件注册

103 阅读3分钟

SpringMVC源码分析之Servlet容器启动和Servlet组件注册

1.自动配置类 DispatcherServletAutoConfiguration

这个配置类中定义了两个bean

  • DispatcherServlet 会拦截所有的请求,并调用相应的控制器处理
  • DispatcherServletRegistrationBean 这个类的主要作用是想Servlet容器中注册Servlet组件
 @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
 public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
     DispatcherServlet dispatcherServlet = new DispatcherServlet();
     dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
     dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
     dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
     dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
     dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
     return dispatcherServlet;
 }
 ​
 @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;
 }

2.自动配置类 ServletWebServerFactoryAutoConfiguration

 @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
         ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, //引入Tomcat容器配置
         ServletWebServerFactoryConfiguration.EmbeddedJetty.class,//引入Jetty容器配置
         ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
 public class ServletWebServerFactoryAutoConfiguration {
 }   
 static class EmbeddedTomcat {
     //创建Tomcat工厂
     @Bean
     TomcatServletWebServerFactory tomcatServletWebServerFactory(
         ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
         ObjectProvider<TomcatContextCustomizer> contextCustomizers,
         ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
         TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
         factory.getTomcatConnectorCustomizers()
             .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatContextCustomizers()
             .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatProtocolHandlerCustomizers()
             .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
         return factory;
     }
 ​
 }

3.启动Tomcat容器

ioc容器创建时会调refresh方法,调用流程如下

 AbstractApplicationContext#refresh()  
     => ServletWebServerApplicationContext#onfrefresh()
         => createWebServer

WebServer创建流程:

 private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
       StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
       ServletWebServerFactory factory = getWebServerFactory();
       createWebServer.tag("factory", factory.getClass().toString());
        //WebServer工厂创建WebServer
       this.webServer = factory.getWebServer(getSelfInitializer());
       createWebServer.end();
       getBeanFactory().registerSingleton("webServerGracefulShutdown",
             new WebServerGracefulShutdownLifecycle(this.webServer));
       getBeanFactory().registerSingleton("webServerStartStop",
             new WebServerStartStopLifecycle(this, this.webServer));
    }
    else if (servletContext != null) {
       try {
          getSelfInitializer().onStartup(servletContext);
       }
       catch (ServletException ex) {
          throw new ApplicationContextException("Cannot initialize servlet context", ex);
       }
    }
    initPropertySources();
 }
 ​
 //向 Tomcat 中注册 Servlet Filter
 //DispatcherServlet 和 CharacterEncodingFilter都是在这里注册到Servlet容器中的
 private void selfInitialize(ServletContext servletContext) throws ServletException {
     prepareWebApplicationContext(servletContext);
     registerApplicationScope(servletContext);
     WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
     for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
         beans.onStartup(servletContext);
     }
 }
 ​

TomcatServletWebServerFactory创建Tomcat服务流程

 public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
       Registry.disableRegistry();
    }
    //创建Tomcat
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
       tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    //启动Tomcat
    return getTomcatWebServer(tomcat);

4.向Tomcat中注册Servlet、Filter

首先自定义一个Filter 和Servlet

 public class MyFilter1 implements Filter {
     @Override
     public void init(FilterConfig filterConfig) throws ServletException {
         System.out.println("MyFilter1的初试化方法");
     }
 ​
     @Override
     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
         System.out.println("MyFilter1的doFilter方法");
         filterChain.doFilter(servletRequest,servletResponse);
     }
 ​
     @Override
     public void destroy() {
         System.out.println("MyFilter1的destroy方法");
     }
 }
 ​
 public class MyServlet extends HttpServlet {
 ​
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         System.out.println("自定义Servlet doGet请求");
         resp.setContentType("text/html");
         ServletOutputStream outputStream = resp.getOutputStream();
         outputStream.write("自定义Servlet doGet请求".getBytes(StandardCharsets.UTF_8));
         outputStream.flush();
         outputStream.close();
 ​
     }
 }
 ​

方式一:使用注解

启动类:

 @ServletComponentScan

自定义Servlet

 @WebServlet(name = "myServlet",urlPatterns = "/myServlet")
 public class MyServlet extends HttpServlet {

自定义Filter

 @WebFilter(filterName = "myFilter1",urlPatterns = "/myServlet")
 public class MyFilter1 implements Filter {

原理分析:

方式二:使用RegistrationBean进行注册

 //注册Filter
 @Bean
 public FilterRegistrationBean<MyFilter1> myFilter(){
     FilterRegistrationBean<MyFilter1> filterRegistrationBean = new FilterRegistrationBean<>();
     filterRegistrationBean.setFilter(new MyFilter1()); //设置自定义Filter
     filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet")); //设置自定义Filter 匹配路径
     return filterRegistrationBean;
 }
 ​
 @Bean
 public ServletRegistrationBean<MyServlet> myServletRegistration() {
     //设置定义Servlet
     ServletRegistrationBean<MyServlet> registration = new ServletRegistrationBean<>(new MyServlet(),"/myServlet");
     registration.setName("myServlet"); 
     return registration;
 }

注册原理分析:

1.从IOC容器中获取全部的ServletContextInitializer接口的实例对象

RegistrationBean 类实现了ServletContextInitializer接口,它的作用是向Servlet容器注册Servlet 、Filter和Listener组件。ServletContextInitializer 接口中有一个onStartup 方法,Servlet容器启动后会回调这个方法,使用这个回调方法就可以将Servlet 和Filter 组件注册到Servlet容器中。

image-20221107203823284.png

FilterRegistrationBean 注册Filter DispatcherServletRegistrationBean 注册DispatcherServlet ServletRegistrationBean 注册Servlet ServletListenerRegistrationBean 注册Listener

Springboot创建tomcat容器是在ServletWebServerApplicationContext#createWebServer方法中进行,通过调用getSelfInitializer方法来获取对应的ServletContextInitializer实例对象:

 private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    registerApplicationScope(servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
       //调用 ServletContextInitializer 的onStartup方法
       beans.onStartup(servletContext);
    }
 }

ServletWebServerApplicationContext#getServletContextInitializerBeans

 protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
     return new ServletContextInitializerBeans(getBeanFactory());
 }

ServletContextInitializerBeans是一个从容器ListableBeanFactory中获取的ServletContextInitializer实例对象集合,包含所有的ServletContextInitializer beans对象,Servlet、Filter、EventListener监听器bean。

ServletContextInitializerBeans#ServletContextInitializerBeans

 public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
                                       Class<? extends ServletContextInitializer>... initializerTypes) {
     this.initializers = new LinkedMultiValueMap<>();
     this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
         : Collections.singletonList(ServletContextInitializer.class);
     //将容器中的ServletContextInitializer实例对象按照类别加入到集合
     addServletContextInitializerBeans(beanFactory); 
     //将容器中的Servlet、Filter、EventListenr实例对象加入集合
     addAdaptableBeans(beanFactory);
     List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
         .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
         .collect(Collectors.toList());
     this.sortedList = Collections.unmodifiableList(sortedInitializers);
     logMappings(this.initializers);
 }
 ​
 @SuppressWarnings("unchecked")
 protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
     MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
     //将容器中的Servlet实例对象转换为RegistrationBean实例对象加入ServletContextInitializer集合
     addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
     //将容器中的Filter实例对象转换为RegistrationBean实例对象加入ServletContextInitializer集合
     addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
     for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
         //将容器中的EventListener实例对象转换为RegistrationBean实例对象加入ServletContextInitializer集合
         addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
                               new ServletListenerRegistrationBeanAdapter());
     }
 }
 //将ServletContextInitializer、Filter、Servlet、EventListener类的实例对象加入到集合中
 private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
                                               ListableBeanFactory beanFactory) {
     if (initializer instanceof ServletRegistrationBean) {
         Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
         //将Servlet加入集合
         addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
     }
     else if (initializer instanceof FilterRegistrationBean) {
         Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
         //将Filter加入集合
         addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
     }
     else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
         String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
         //将Servlet加入集合
         addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
     }
     else if (initializer instanceof ServletListenerRegistrationBean) {
         EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
         //将EventListener加入集合
         addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
     }
     else {
         //将ServletContextInitializer加入集合
         addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
                                          initializer);
     }
 }
2.注册组件

遍历所有的ServletContextInitializer 接口的实例对象,并调用onStartup 方法。

RegistrationBean#onStartup

 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);
 }

DynamicRegistrationBean#register

 protected final void register(String description, ServletContext servletContext) {
    //将组件添加到ServletContext 中 
    D registration = addRegistration(description, servletContext);
    if (registration == null) {
       logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
       return;
    }
    //配置请求路径
    // DispatcherServlet 和 CharacterEncodingFilter 请求路径都是在这里拦截的
    configure(registration);
 }
 @Override
 protected void configure(ServletRegistration.Dynamic registration) {
     super.configure(registration);
     String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
     if (urlMapping.length == 0 && this.alwaysMapUrl) {
         urlMapping = DEFAULT_MAPPINGS; 
     }
     if (!ObjectUtils.isEmpty(urlMapping)) {
         registration.addMapping(urlMapping);
     }
     registration.setLoadOnStartup(this.loadOnStartup);
     if (this.multipartConfig != null) {
         registration.setMultipartConfig(this.multipartConfig);
     }
 }