美文网首页spring boot
spring-boot Tomcat初始化源码分析

spring-boot Tomcat初始化源码分析

作者: 慢狍子 | 来源:发表于2016-11-12 19:37 被阅读653次

    基本实现说明

    spring-boot-starter-tomcat默认会被spring-boot-starter所依赖,而spring-boot实现的巧妙方式在于,通过依赖判定当前classpath下是否存在Tomcat类来断定,当前web容器类型。
    这种方式也是spring-boot实现通过配置来决定启用何种服务的根本原理。

    代码分析

    spring-boot容器的默认超类是EmbeddedWebApplicationContext,而真正初始化容器的过程是调用createEmbeddedServletContainer方法。
    EmbeddedWebApplicationContext

        private void createEmbeddedServletContainer() {
            EmbeddedServletContainer localContainer = this.embeddedServletContainer;
            ServletContext localServletContext = getServletContext();
            if (localContainer == null && localServletContext == null) {
                EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
                // 从容器中查找实例,方法如下
                this.embeddedServletContainer = containerFactory
                        .getEmbeddedServletContainer(getSelfInitializer());
            }
            else if (localServletContext != null) {
                try {
                    getSelfInitializer().onStartup(localServletContext);
                }
                catch (ServletException ex) {
                    throw new ApplicationContextException("Cannot initialize servlet context",
                            ex);
                }
            }
            initPropertySources();
        }
    
        protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
            // Use bean names so that we don't consider the hierarchy
            String[] beanNames = getBeanFactory()
                    .getBeanNamesForType(EmbeddedServletContainerFactory.class);
            ....
            ....
        }
    

    即查找类型为EmbeddedServletContainerFactory.class的对象,而此对象的BeanDefinition放入的时机则是通过配置来实现的。

    其中有一个过程是通过EnableAutoConfigurationImportSelector查找在在spring.factories中配置的所有类型为org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置类,而这些默认实现类在spring-boot-starter.autoconfigure中,截取部分如下

    ...
    ...
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
    org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
    org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
    org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
    org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
    ...
    org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
    org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
    org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
    \
    ....
    org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
    org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
    org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
    ...
    

    查看其中的org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication
    @Import(BeanPostProcessorsRegistrar.class)
    public class EmbeddedServletContainerAutoConfiguration {
    
        @Configuration
        @ConditionalOnClass({ Servlet.class, Tomcat.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedTomcat {
    
            @Bean
            public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
                return new TomcatEmbeddedServletContainerFactory();
            }
    
        }
    
        @Configuration
        @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
                WebAppContext.class })
        @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
        public static class EmbeddedJetty {
    
            @Bean
            public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
                return new JettyEmbeddedServletContainerFactory();
            }
        }
        ...
        ...
    
    }
    
    

    这个提供了默认三种web容器,Tomcat,Jetty,Undertow,而@ConditionalOnClass注解的配置决定了在什么情况下使用这个类,类似Profile的功能。此条件判断依据为当前classpath容器中存在是否存在配置的class,以EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat来说,它存在的依据在于当前classpath存在Servlet类和Tomcat类,而spring-boot默认依赖Tomcat,所以EmbeddedTomcat将被激活,而EmbeddedServletContainerAutoConfiguration$EmbeddedJetty将不会被采用。
    所以,如果你想要使用jetty容器,仅仅需要将Tomcat依赖排除,并添加Jetty依赖即可,其他容器同理。
    此时容器中已经确定了web容器,初始化的代码就在TomcatEmbeddedServletContainerFactory#getEmbeddedServletContainer

    public EmbeddedServletContainer getEmbeddedServletContainer(
                ServletContextInitializer... initializers) {
            Tomcat tomcat = new Tomcat();
            File baseDir = (this.baseDirectory != null ? this.baseDirectory
                    : createTempDir("tomcat"));
            tomcat.setBaseDir(baseDir.getAbsolutePath());
            Connector connector = new Connector(this.protocol);
            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);
            return getTomcatEmbeddedServletContainer(tomcat);
        }
    

    相关文章

      网友评论

      本文标题:spring-boot Tomcat初始化源码分析

      本文链接:https://www.haomeiwen.com/subject/lqyqpttx.html