我们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 {}