zoukankan      html  css  js  c++  java
  • 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”字节指令,关闭非守护线程,从而关闭守护线程

     

  • 相关阅读:
    算法-动态规划 Dynamic Programming--从菜鸟到老鸟
    DTW动态时间规整
    安装splash
    安装 Tesserocr (填坑)
    pip3 install tesserocr安装失败(已解决)
    从头到尾彻底理解傅里叶变换算法
    ruby之——安装gem提示:Please update your PATH to include build tools or download the DevKit
    关于0x80000000为什么等于-2147483648和负数在内存上储存的问题
    html5 canvas
    html5 视频和音频
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12592328.html
Copyright © 2011-2022 走看看