SpringBoot-SpringMVC的启动

1.Tomcat预留的接口

1.继承ServletContainerInitializer
这个是启动会被调用的接口

public interface ServletContainerInitializer {

    /**
     * Receives notification during startup of a web application of the classes
     * within the web application that matched the criteria defined via the
     * {@link javax.servlet.annotation.HandlesTypes} annotation.
     *
     * @param c     The (possibly null) set of classes that met the specified
     *              criteria
     * @param ctx   The ServletContext of the web application in which the
     *              classes were discovered
     *
     * @throws ServletException If an error occurs
     */
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

2.在META-INF/services下增加
采用java的SPI机制加载,参考org.springframework.spring-web.jar
javax.servlet.ServletContainerInitializer

org.springframework.web.SpringServletContainerInitializer

https://blog.csdn.net/weixin_38937840/article/details/107773717

tomcat和jetty切换原理
https://www.cnblogs.com/fanshuyao/p/8668059.html

2.SpringBoot自己创建容器

如果是jetty或者tomcat启动,会有个配置

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
   
   // 判断是否由tomcat,有则自动注入tomcat相关的创建bean工厂
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat {
      
        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(
                ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
                ObjectProvider<TomcatContextCustomizer> contextCustomizers,
                ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            return factory;
        }

    }

    // 判断是否有jetty,有则自动注入jetty相关的创建bean工厂
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedJetty {

        @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(
                ObjectProvider<JettyServerCustomizer> serverCustomizers) {
            JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
            factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

    }

    // 判断是否有Undertow,有则自动注入jetty相关的创建bean工厂
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedUndertow {

        @Bean
        UndertowServletWebServerFactory undertowServletWebServerFactory(
                ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
                ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
            UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
            factory.getDeploymentInfoCustomizers()
                    .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

        @Bean
        UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
                ServerProperties serverProperties) {
            return new UndertowServletWebServerFactoryCustomizer(serverProperties);
        }

    }

}

这个类里面,根据类的加载情况,会自动注入容器的的创建工厂。

启动流程:
1.springboot启动时候,判断上下文


switch (webApplicationType) {
  case SERVLET:
    return new AnnotationConfigServletWebServerApplicationContext();
  case REACTIVE:
    return new AnnotationConfigReactiveWebServerApplicationContext();
  default:
       return new AnnotationConfigApplicationContext();
}

2.看类的继承


image.png

3.我们可以在父类ServletWebServerApplicationContext中找到,这样一个函数

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
          // 创建web容器
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

这里就是创建的web容器,而这个函数会在spring刷新的时候被调用,这也是spring留给子类的拓展点。

4.进入这个函数

private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            // 获取servletWebServerFactory
            ServletWebServerFactory factory = getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
             // 创建web的服务器
            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();
    }

4.这里的ServletWebServerFactory你会发现就是用于创建之前ServletWebServerFactoryConfiguration里面配置的tomcat、jetty这些的创建工厂,所以我们先看一下tomcat是如何实现getWebServer

5.创建tomcat

    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        // 1.new tomcat对象
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        
       // 2.设置Connector
       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);
    }

这里再getTomcatWebServer后面就是调用tomcat的start,真正的把tomcat启动了。

总结:springboot对于内置的tomcat或者jetty,其实是通过@Configuration,判断是否有jetty或者tomcat的类,找到后会注入一个创建tomcat或者jetty的工厂。在后续的spring创建的时候,会在OnRefresh的时候,创建web容器。

4.WebMvcAutoConfiguration

springboot对于springmvc启动的配置类,梦开始的地方..,通过里面的EnableWebMvcConfiguration,继承了最终的WebMvcConfigurationSupport。这个Support类,就是会把SpringMVC需要的Bean注入到Spring中,例如:
RequestMappingHandlerAdapter(对参数的解析,返回值解析,处理请求的类),

RequestMappingHandlerMapping(请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors))

WebMvcAutoConfiguration这个类里面有一个EnableWebMvcConfiguration,这个类又继承了DelegatingWebMvcConfiguration

下图是这些类的继承关系:


image.png

然后我们看看DelegatingWebMvcConfiguration有什么东西,可以看到下面其实基本上都是回调WebMvcConfigurer的接口,而WebMvcConfigurer就是给我们去自定义比如拦截器,等等的地方。

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

   // 设置配置器
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }

    @Override
    protected void addFormatters(FormatterRegistry registry) {
        this.configurers.addFormatters(registry);
    }
  
   // 添加拦截器
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        this.configurers.addInterceptors(registry);
    }
   
   // .....
    
   // 添加参数解析器
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        this.configurers.addArgumentResolvers(argumentResolvers);
    }
  

   //  添加消息转换器
    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.configureMessageConverters(converters);
    }

}

所以如果我们想自定义一些拦截器就像下面这样
自定义WebMvcConfigurer的例子:

@Slf4j
@Configuration
public class BootWebFilterConfiguration {

    @Bean
    WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {、
                registry.addInterceptor(handlerInterceptor());
            }
        };
    }

    HandlerInterceptor handlerInterceptor() {
        return new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                log.info("jiang~~ preHandle");
                return true;
            }
        };
    }
}

5.写在最后

通过上面的代码分析,我们知道了SpringMVC在SpringBoot中是如何启动,同时还有一个重要的地方,就是如何自定义WebMvcConfigurer。以后再实现就不要去继承WebMvcConfigurationSupport

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容