AI智能
改变未来

springboot源码解析-管中窥豹系列之web服务器(七)


一、前言

  • Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去。
  • 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot源码管中窥豹系列。

二、web服务器

  • 以前的的spring项目或者springmvc项目都需要一个web服务器,tomcat,或者其它的
  • 使用springboot之后,我们不再需要配置web服务器,因为springboot帮我们集成了
  • 今天我们来分析一下源码,看看在哪里实现的,知其然知其所以然

三、源码分析

  • 还是从SpringApplication的run方法开始看
  • 不熟悉的可以看之前的文章:springboot源码解析-管中窥豹系列之总体结构(一)
SpringApplication.javapublic ConfigurableApplicationContext run(String... args) {...try {...refreshContext(context);...}catch (Throwable ex) {...}...return context;}
  • 接着进入到 refreshContext(context) 里面
AbstractApplicationContext.java@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {...try {...// Initialize other special beans in specific context subclasses.onRefresh();...}catch (BeansException ex) {...}finally {...}}}
  • 进入到 onRefresh() 方法
protected void onRefresh() throws BeansException {// For subclasses: do nothing by default.}
  • 注意这个是一个protected方法,我们进入到子实现里面
  • 具体用的哪个context,请看之前的文章:springboot源码解析-管中窥豹系列之项目类型(二)
public static final String DEFAULT_CONTEXT_CLASS = \"org.springframework.context.\"+ \"annotation.AnnotationConfigApplicationContext\";public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = \"org.springframework.boot.\"+ \"web.servlet.context.AnnotationConfigServletWebServerApplicationContext\";public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = \"org.springframework.\"+ \"boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext\";protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException(\"Unable create a default ApplicationContext, please specify an ApplicationContextClass\", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
  • 最常用的就是普通web项目,我们看这一个
  • 我们到org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext里面找 onRefresh() 方法
  • 没找到,在父类ServletWebServerApplicationContext里面找到了
ServletWebServerApplicationContext.java@Overrideprotected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException(\"Unable to start web server\", ex);}}
  • 我们到 createWebServer() 方法里面
private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());}else ifad8(servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException(\"Cannot initialize servlet context\",ex);}}initPropertySources();}
  • 用的工厂模式,先找到工厂getWebServerFactory()
  • 再用工厂生成webServer, factory.getWebServer(getSelfInitializer())
  • 先看看 getWebServerFactory() 这个方法
protected ServletWebServerFactory getWebServerFactory() {// Use bean names so that we don\'t consider the hierarchyString[] 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);}
  • 获取唯一的工厂:ServletWebServerFactory,多了少了都不行
  • 在哪加载进springboot的呢?
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><scope>compile</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><scope>compile</scope></dependency>
  • spring-boot-starter-web里面是包含了spring-boot-starter依赖的
  • spring-boot-starter里面包含了spring-boot-autoconfigure依赖
  • spring-boot-autoconfigure里面有一个类:ServletWebServerFactoryConfiguration
  • 这个类里面有一个静态类: EmbeddedTomcat
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServlet56cWebServerFactory(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;}}
  • ConditionOnClass : 它依赖 Servlet.class, Tomcat.class, UpgradeProtocol.class
  • ConditionalOnMissingBean: 防止重复加载
  • 至此,factory怎么加载进spring就找到了
  • 我们多看一点,这个类ServletWebServerFactoryConfiguration还有两个静态类
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRad0ENT)static class EmbeddedJetty {@BeanJettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {JettyServletWebServerFactory factory = new JettyServletWebServerFactory();factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedUndertow {@BeanUndertowServletWebServerFactory 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;}}
  • 一个是生成jetty服务器工厂,一个是生成undertow服务器工厂
  • 它们的加载,取决于依赖的class是否存在
  • tomcat: Servlet.class, Tomcat.class, UpgradeProtocol.class
  • jetty: Servlet.class, Server.class, Loader.class, WebAppContext.class
  • undertow: Servlet.class, Undertow.class, SslClientAuthMode.class

假如我们想换成undertow服务器,依赖改了就行了

<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-undertow</artifactId></dependency>

假如我们想换成jetty服务器,同理

<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>1602</exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency>
  • 思维拉回来,工厂有了,我们看看工厂怎么生成的webServer
TomcatServletWebServerFactory.java@Overridepublic WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new 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);return getTomcatWebServer(tomcat);}
  • 这个方法我们就不分析了,就是生成tomcat服务器,和spring关联不大,改天我们专门分析tomcat源码
  • 至此,整个springboot的加载web服务器过程就完了

欢迎关注微信公众号:丰极,更多技术学习分享。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » springboot源码解析-管中窥豹系列之web服务器(七)