一、前言

  • 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 if (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 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);}
  • 获取惟一的工厂: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 {    @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;    }}
  • 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.CURRENT)static class EmbeddedJetty {    @Bean    JettyServletWebServerFactory 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 {    @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;    }}
  • 一个是生成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>        </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服务器过程就完了

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