我們SpringBoot應用都是透過SpringApplication.run()
這一行程式碼啟動起來的,那麼我們有理由懷疑實現邏輯就在這個裡面
應用跑起來-run()
public ConfigurableApplicationContext run(String... args) { // 省略程式碼 ... ConfigurableApplicationContext context = null; // 省略程式碼 ... try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 準備上下文環境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 配置忽略的Bean資訊 configureIgnoreBeanInfo(environment); // 列印Banner資訊 Banner printedBanner = printBanner(environment); // 建立ioc容器 context = createApplicationContext(); // 準備ioc容器【核心】 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 重新整理ioc容器 refreshContext(context); // 省略程式碼 ... } // 省略程式碼 ... return context; }
重新整理ioc容器-refreshContext()
private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } // 進一步呼叫 refresh() refresh((ApplicationContext) context); } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); // 進一步呼叫 refresh() refresh((ConfigurableApplicationContext) applicationContext); } // 呼叫 ConfigurableApplicationContext 的refresh() protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); }
我們可以發現最終呼叫了ConfigurableApplicationContext
的refresh()
來完成重新整理容器的操作
ServletWebServerApplicationContext
public final void refresh() throws BeansException, IllegalStateException { try { // 呼叫父類的 refresh() super.refresh(); } catch (RuntimeException ex) { // 如果出現異常,並且web伺服器建立好了,就會停止當前web伺服器 WebServer webServer = this.webServer; if (webServer != null) { webServer.stop(); } throw ex; } }
AbstractApplicationContext
Spring容器重新整理經典12大步
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 1、準備重新整理 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 2、獲取一個BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 3、準備BeanFactory prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 4、BeanFactory的後置處理【留給子類的擴充套件】 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 5、執行BeanFactory的後置處理器 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 6、註冊Bean的後置處理器 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 7、初始化MessageSource【國際化相關】 initMessageSource(); // Initialize event multicaster for this context. // 8、初始化事件派發工具 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 9、重新整理【留給子類的擴充套件點,重點關注】 onRefresh(); // Check for listener beans and register them. // 10、主備監聽器 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 11、完成BeanFactory的初始化工作【例項化剩下的單例項Bean】 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 12、重新整理完成【釋出事件】 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... // 重置快取 resetCommonCaches(); } } }
我們重點關注第9步 onRefresh()
,因為這是留給子類的擴充套件點
子類ioc容器的擴充套件點-onRefresh()
ServletWebServerApplicationContext
protected void onRefresh() { // 1、呼叫父類的 onRefresh() super.onRefresh(); try { // 2、建立Web伺服器 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
建立Web伺服器-createWebServer()
這裏拿到的伺服器工廠預設是Tomca
t的,所以會建立出Tomcat
伺服器的例項然後啟動
因為我們引入的starter-web
裡預設引入的就是Tomcat
的jar包
private void createWebServer() { // 1、拿到Web伺服器【剛開始肯定是null,因為我們還沒建立呢】 WebServer webServer = this.webServer; // 2、拿到ServletContext【剛開始也是null】 ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { // 3、從容器中得到一個Web伺服器的工廠【ServletWebServerFactory】 ServletWebServerFactory factory = getWebServerFactory(); // 4、使用工廠來建立Web伺服器 this.webServer = factory.getWebServer(getSelfInitializer()); getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { // 遍歷所有的ServletContextInitializer呼叫其onStartup()來啟動 getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
getWebServer()
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } // 1、直接建立一個Tomcat伺服器 Tomcat tomcat = new Tomcat(); // 2、設定一些Tomcat的屬性 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); 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); // 3、得到一個Tomcat伺服器【會啟動Tomcat伺服器】 return getTomcatWebServer(tomcat); } protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown()); }
建立一個TomcatWebServer
,用來封裝Tomat
例項
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null; // 初始化:啟動Tomcat initialize(); } private void initialize() throws WebServerException { logger.info("Tomcat initialized with port(s): " + getPortsDescription(false)); synchronized (this.monitor) { try { // 省略程式碼 ... // 啟動tomcat this.tomcat.start(); // 省略程式碼 ... } catch (Exception ex) { // 出現異常就停止、銷燬Tomcat stopSilently(); destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
到這裏Tomcat就建立並啟動成功了。
如何切換Web伺服器
案例:切換Web伺服器為Jetty。
我們知道了內嵌伺服器的原理,它是透過不同的Web伺服器工廠來建立出不同的Web伺服器例項,然後啟動。
Web伺服器工廠透過條件註解進行註冊到容器中,然後從容器中拿到Web伺服器工廠,也就是匯入不同的jar包就會註冊不同的Web伺服器工廠。所以我們只需要把tomcat的jar包替換為jetty的jar包即可完成切換。
Tomcat
的jar包是在starter-web
中引入的:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.3.12.RELEASE</version> <scope>compile</scope> </dependency>
我們只需要把這個jar包排除,然後加上jetty的jar包即可實現,SpringBoot幫我們把一切都在底層配置好了
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
再次啟動,會發現已經切換成功了:
這裏從容器中拿到的Web伺服器工廠就是Jetty的了:
總結
我們發現內嵌Tomcat
伺服器的本質其實是:透過Web伺服器工廠建立一個Tomcat例項,然後呼叫其start()
來啟動。
其它伺服器也是一樣的道理
// 1、web伺服器工廠建立伺服器例項 factory.getWebServer(getSelfInitializer()) // 2、建立 Tomcat tomcat = new Tomcat(); // 啟動 tomcat.start();
Web伺服器工廠如何被註冊到容器中的?
使用ServletWebServerFactoryAutoConfiguration
自動配置類
利用@Import()
機制給容器中匯入元件,然後透過@ConditonalOnxxx()
條件註解給容器中匯入不同的Web伺服器工廠
@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { } // 嵌入式的Tomcat @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat { // Tomcat Web伺服器工廠 @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } // 嵌入式的Jetty @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedJetty { // Jetty Web伺服器工廠 @Bean JettyServletWebServerFactory JettyServletWebServerFactory( ObjectProvider<JettyServerCustomizer> serverCustomizers) { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } // 嵌入式的Undertow @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedUndertow { // Undertow Web伺服器工廠 @Bean UndertowServletWebServerFactory undertowServletWebServerFactory( ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers, ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) { UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.getDeploymentInfoCustomizers() .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList())); factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList())); return factory; } }
WebServerFactory
用來建立Web伺服器的工廠
WebServer
Web伺服器
WebServer實際是呼叫具體的伺服器例項來做的:Tomcat、Jetty、Undertow
public class Tomcat {} public class Jetty {}