种类
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容器实例化在
spring-boot支持两种容器方案,一种为阻塞式的servlet,另一种为非阻塞的reactive,spring-boot在启动时会根据classpath下文件推断当前使用的是何种容器方案,从而使用
onRefresh
方法里进行。spring-boot支持两种容器方案,一种为阻塞式的servlet,另一种为非阻塞的reactive,spring-boot在启动时会根据classpath下文件推断当前使用的是何种容器方案,从而使用
ReactiveWebServerApplicationContext
或ServletWebServerApplicationContext
类实例,它们都继承了spring-framework的GenericWebApplicationContext
类,并重写了onRefresh
方法。servlet方案容器的实例化
ServletWebServerApplicationContext.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@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); }
容器类型存在成员变量
上一段提到需要从beanFactory中获取实现了
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
方法。该方法直接调用了
webServer
的start
方法启动容器。就这样,项目容器的WebServer
接口实现类便会被启动,具体的启动方法因容器而异,具体启动的实现可参考NettyWebServer
、TomcatWebServer
、JettyWebServer
、UndertowWebServer
、UndertowServletWebServer
,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方法运行即可。