SpringBoot——tomcat嵌入

SpringBoot——启动与自动配置类查找中启动过程,已经找到了tomcat启动在于AbstractApplicationContext.refresh()的onRefresh()方法中,即ServletWebServerApplicationContext.onRefresh()方法中。

下面先回顾下tomcat+springMVC启动web服务的流程实现,然后在学习springboot中的tomcat嵌入。

一、tomcat+SpringMVC启动web服务

Web服务的核心流程其实就是servlet的生命周期:

① tomcat启动,解析web.xml文件中DispatcherServlet生成Servlet对象,放入到tomcat容器中

② 调用DispatcherServlet.init():初始化SpringIOC容器;初始化SpringMVC的9个组件并放入到SpringIOC容器中。

③ 响应服务DispatcherServlet.service()

④ 容器关闭DispatcherServlet.destory()

其中②③是需要具体了解的,前面SpringMVC——DispatcherServlet中已经学习过②中9个组件初始化+③servlet.service()实现,但是还没有明确②中springIOC容器与Servlet的关系

下面借用官网的一个例子说明:

a、创建一个SpringMVC的IOC容器WebApplicationContext,并初始化WebApplicationContext.refresh()(这里有个WebApplicationContext.setparent(rootApplicationContext))

b、将WebApplicationContext绑定到DispatcherServlet中

c、初始化SpringMVC的9个组件(包括HandlerMapping,HandlerAdapters等)并放入到WebApplicationContext容器中。

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        //创建一个SpringMVC的容器WebApplicationContext,并初始化
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // 将springMVC的容器WebApplicationContext绑定到DispatcherServlet中
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

综上:如果我们要实现tomcat的嵌入,可能需要实现:

① 需要new Tomcat、需要new DispatcherServlet、需要new WebApplicaitonContext

② 然后将webApplicationContext绑定到DispatcherServlet中,将DispatcherServlet放入到Tomcat中。

伪代码实现流程:

ApplicationContext context = new ApplicationContext();
context.refresh();
DispatcherServlet servlet = new DispathcherServlet(context);
Tomcat tomcat = new Tomcat();
tomcat.addServlet(servlet);//主要是这一步
tomcat.start();

二、tomcat嵌入

ServletWebServerApplicationContext.onRefresh():tomcat启动入口

/*org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh */
    protected void onRefresh() {
        //父类方法,设置主题资源
        super.onRefresh();
        try {  
            //tomcat启动
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            //自动配置类ServletWebServerFactoryAutoConfiguration
            //@Import ServletWebServerFactoryConfiguration.EmbeddedTomcat.class
            //初始化TomcatServletWebServerFactory到SpringIOC容器中
            //注意初始化多个ServletWebServerFactory会报错
            ServletWebServerFactory factory = getWebServerFactory();
            //工厂方法创建webserver
            //Tomcat tomcat = new tomcat();
            //tomcat的属性修改
            //tomcat.start()
            //另启动一个端口接受tomcat停止命令
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {
                //
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        //根据environment初始化servletContext的属性
        initPropertySources();
    }

TomcatServletWebServerFactory.getWebServer(getSelfInitializer()):

/* org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer */
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        //创建tomcat,默认端口8080,new出来
        Tomcat tomcat = new Tomcat();
        //创建临时路径,存放tomcat的log日志和dump文件,可用server.tomcat.basedir配置路径
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        //创建Connector,协议http1.1,
//Connector.setPort("server.port"),server.port生效的地方
//connector编码encoding、SSL(https)支持
//server.xml 中<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector);
//server.xml 中<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" /> tomcat.getHost().setAutoDeploy(
false);
//server.xml 中<Engine name="Catalina" defaultHost="localhost"></Engine> configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) { //添加自定义的Connector到tomcat中,不会有
//server.xml <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }

getTomcatWebServer():

    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        //创建一个tomcat的web服务,
        //tomcat服务启动tomcat.start()
        //创建一个非阻塞守护线程,BIO监听端口8005,接受字节"SHUTDOWN",停止当前tomcat
        return new TomcatWebServer(tomcat, getPort() >= 0);
    }

    public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        initialize();
    }

    private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
        synchronized (this.monitor) {
            try {
                addInstanceIdToEngineName();

                Context context = findContext();
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                        // Remove service connectors so that protocol binding doesn't
                        // happen when the service is started.
                        removeServiceConnectors();
                    }
                });

                // tomcat启动
                this.tomcat.start();
                rethrowDeferredStartupExceptions();
                try {
                    ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
                }
                catch (NamingException ex) {
                }
// 与Jetty不同,tomcat线程都是守护线程,我们创建一个阻塞的非守护线程来关闭它。
                // (守护线程在所有非守护线程关闭后,会自动关闭,所以这里非守护线程最重要的作用是维持tomcat非守护线程的运行)。
                startDaemonAwaitThread();
            }
            catch (Exception ex) {
                stopSilently();
                destroySilently();
                throw new WebServerException("Unable to start embedded Tomcat", ex);
            }
        }
    }

以上就是tomcat启动流程。tomcat.start()会执行this.webServer = factory.getWebServer(getSelfInitializer());中的getSelfInitializer()

    private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
        return this::selfInitialize;
    }

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        //rootApplicationContext.setServletContext(servletContext)
        //servletContext.setAttribute(rootApplicationContext)
        //这里将webApplicationContext作为rootApplicationContext放入到了ServletContext中
        //后面DispatcherServlet.init()时,会初始化一个applicationContext
        //然后applicationContext.setParent(ServletContext.getAttribute(rootApplicationContext))
        prepareWebApplicationContext(servletContext);
        //注册Servlet作用域application到BeanFactory中,前面研究springboot启动时注入过session+request
        registerApplicationScope(servletContext);
        //注册servlet环境的bean到BeanFactory中
        //servletContext,servletConfig,contextAttributes
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {            beans.onStartup(servletContext);
        }
    }

 getServletContextInitializerBeans():获取BeanFactory中的DispatcherServlet

/* org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getServletContextInitializerBeans */
    protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
        return new ServletContextInitializerBeans(getBeanFactory());
    }

    public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
            Class<? extends ServletContextInitializer>... initializerTypes) {
        this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
                : Collections.singletonList(ServletContextInitializer.class);
        //initializerTypes = [ServletContextInitializer.class]
        //扫描BeanFactory中的ServletContextInitializer.class类型的Bean放入到initializers中
        //DispatcherServletRegistrationBean
        //FilterRegistrationBean
        addServletContextInitializerBeans(beanFactory);
        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);
    }

addServletContextInitializerBeans(beanFactory):

/* org.springframework.boot.web.servlet.ServletContextInitializerBeans#addServletContextInitializerBeans */
    private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
        for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
            for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, initializerType)) {
                addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
            }
        }
    }

    private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
            ListableBeanFactory beanFactory) {
        if (initializer instanceof ServletRegistrationBean) {
            //扫描Servlet到initializers中,"dispatcherServlet"
            Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
            addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof FilterRegistrationBean) {
            //扫描filter到initializers中,
            Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
            addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
            String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
            addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
        }
        else if (initializer instanceof ServletListenerRegistrationBean) {
            EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
            addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
        }
        else {
            addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer);
        }
    }

DispatcherServletRegistrationBean初始化:

@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的属性,
            // WebMvcProperties对应application.properties中的"spring.mvc.dispatchOptionsRequest"
            // spring.mvc.dispatch_options_request = true
            // spring.mvc.dispatch_trace_request = false
            // spring.mvc.throw_exception_if_no_handler_found = false
            // spring.mvc.publish_request_handled_events = true
            // spring.http.log_request_details = false
            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;
        }

    }

    @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) {
            //封装dispatcherServlet实例,
            //spring.mvc.servlet.path = /
            //spring.mvc.servlet.load-on-startup = -1
            //multipartConfg
            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;
        }

    }
  //.......
}

总结:

① tomcat嵌入:是AbstactApplicationContext.onRefresh()中启动的

Tomcat tomcat = new Tomcat();
//server.xml的属性设置到tomcat
//包括server.port设置到Connector中
tomcat.start();
//start启动会执行selfInitialize(),tomcat--dispatcherServlet--springbootIOC容器,的绑定都是在selfInitalize()里面实现的,
// ① ServletContext.setAttribute("rootApplicationContext",springbootApplicationContext)
// ② 扫描beanfactory中的ServletContextInitializer.class类型Servlet/Filter // ③ DispatcherServletRegistrationBean中dispatcherServlet放入了tomcat中
// ④ dispatcherServlet.init()时new WebApplicationContext().setParent(servletContext.getAttribute("rootApplicationContext"))

②tomcat.start()启动是一个监听8080端口的守护线程(非阻塞NIO),后面启动了一个非守护线程监听8005端口(阻塞BIO),作用:

  • 当守护线程关闭后,非守护线程会全部关闭。开一个非守护线程(8005),可维持守护线程(8080)一直运行。
  • 非守护线程(8005)还会接受"SHUTDOWN”字节指令,关闭非守护线程,从而关闭守护线程

 

posted on 2020-03-29 14:58  FFStayF  阅读(395)  评论(0编辑  收藏  举报