嵌入式web容器

种类

spring-boot支持使用jetty、netty、tomcat、undertow作为嵌入式web容器

获取web容器实例

        public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

    protected void onRefresh() throws BeansException {
    }

    protected void finishRefresh() {
        this.clearResourceCaches();
        this.initLifecycleProcessor();
        this.getLifecycleProcessor().onRefresh();
        this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
        LiveBeansView.registerApplicationContext(this);
    }
  spring-framework初始化的步骤如上,而spring-boot的嵌入式web容器实例化在onRefresh方法里进行。
spring-boot支持两种容器方案,一种为阻塞式的servlet,另一种为非阻塞的reactive,spring-boot在启动时会根据classpath下文件推断当前使用的是何种容器方案,从而使用ReactiveWebServerApplicationContextServletWebServerApplicationContext类实例,它们都继承了spring-framework的GenericWebApplicationContext类,并重写了onRefresh方法。

servlet方案容器的实例化

ServletWebServerApplicationContext.java

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            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) {
            ServletWebServerFactory factory = getWebServerFactory();
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context",
                        ex);
            }
        }
        initPropertySources();
    }

        protected ServletWebServerFactory getWebServerFactory() {
        // Use bean names so that we don't consider the hierarchy
        String[] beanNames = getBeanFactory()
                .getBeanNamesForType(ServletWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException(
                    "Unable to start ServletWebServerApplicationContext due to missing "
                            + "ServletWebServerFactory bean.");
        }
        if (beanNames.length > 1) {
            throw new ApplicationContextException(
                    "Unable to start ServletWebServerApplicationContext due to multiple "
                            + "ServletWebServerFactory beans : "
                            + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }
ServletWebServerApplicationContext.java
  容器类型存在成员变量webServer中,在实例化容器前,会先判断webServer是否已经被实例化,webServer是一个接口,所有的嵌入式容器启动类都实现了这个接口。
getWebServerFactory方法会从beanFactory中获取实现了ServletWebServerFactory接口的类实例,通过其getWebServer方法可以实例化具体的web容器实例并放入实现了WebServer的类中返回。
上一段提到需要从beanFactory中获取实现了ServletWebServerFactory接口的类实例,这个类实例来自于spring-boot提供的auto-configuration功能,专用的嵌入式容器jar包会自动配置ServletWebServerFactory的实现。

servlet方案容器的运行

        @Override
    protected void finishRefresh() {
        super.finishRefresh();
        WebServer webServer = startWebServer();
        if (webServer != null) {
            publishEvent(new ServletWebServerInitializedEvent(webServer, this));
        }
    }

        private WebServer startWebServer() {
        WebServer webServer = this.webServer;
        if (webServer != null) {
            webServer.start();
        }
        return webServer;
    }
  上文说到容器的实例化在onRefresh方法进行,而容器的启动则是在finishRefresh方法进行,同样的,ServletWebServerApplicationContext类重写了GenericWebApplicationContext类的finishRefresh方法。
该方法直接调用了webServerstart方法启动容器。就这样,项目容器的WebServer接口实现类便会被启动,具体的启动方法因容器而异,具体启动的实现可参考NettyWebServerTomcatWebServerJettyWebServerUndertowWebServerUndertowServletWebServer,spring-boot提供了这几种嵌入式容器启动的实现。

reactive方案容器的实例化

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

       private void createWebServer() {
        ServerManager serverManager = this.serverManager;
        if (serverManager == null) {
            this.serverManager = ServerManager.get(getWebServerFactory());
        }
        initPropertySources();
    }

        protected ReactiveWebServerFactory getWebServerFactory() {
        // Use bean names so that we don't consider the hierarchy
        String[] beanNames = getBeanFactory()
                .getBeanNamesForType(ReactiveWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException(
                    "Unable to start ReactiveWebApplicationContext due to missing "
                            + "ReactiveWebServerFactory bean.");
        }
        if (beanNames.length > 1) {
            throw new ApplicationContextException(
                    "Unable to start ReactiveWebApplicationContext due to multiple "
                            + "ReactiveWebServerFactory beans : "
                            + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        return getBeanFactory().getBean(beanNames[0], ReactiveWebServerFactory.class);
    }
  与servlet方案的容器实例化类似,reactive方案的容器实例化仅仅将获取容器实例的接口从ServletWebServerFactory变成了ReactiveWebServerFactory,嵌入式容器jar包会提供实现了ServletWebServerFactory接口的bean。

reactive方案容器的运行

        @Override
    protected void finishRefresh() {
        super.finishRefresh();
        WebServer webServer = startReactiveWebServer();
        if (webServer != null) {
            publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));
        }
    }

        private WebServer startReactiveWebServer() {
        ServerManager serverManager = this.serverManager;
        ServerManager.start(serverManager, this::getHttpHandler);
        return ServerManager.getWebServer(serverManager);
    }

        public static void start(ServerManager manager,
                Supplier<HttpHandler> handlerSupplier) {
            if (manager != null && manager.server != null) {
                manager.handler = handlerSupplier.get();
                manager.server.start();
            }
        }

与servlet方案容器的实现一致,直接调用start方法运行即可。



博客园:https://www.cnblogs.com/xianquan
Copyright ©2020 l-coil
【转载文章务必保留出处和署名,谢谢!】
【推广】 免费学中医,健康全家人
原文地址:https://www.cnblogs.com/xianquan/p/13018813.html