当前位置 博文首页 > 丰极:springboot源码解析-管中窥豹系列之web服务器(七)

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

    作者:丰极 时间:2021-02-04 18:21

    一、前言

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

     简介

    二、web服务器

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

    三、源码分析

    • 还是从SpringApplication的run方法开始看
    • 不熟悉的可以看之前的文章:springboot源码解析-管中窥豹系列之总体结构(一)
    SpringApplication.java
    public ConfigurableApplicationContext run(String... args) {
     
        ...
    
        try {
            
            ...
    
            refreshContext(context);
            
            ...
    
        }
        catch (Throwable ex) {
            
            ...
    
        }
    
        ...
    
        return context;
    }
    
    • 接着进入到 refreshContext(context) 里面
    AbstractApplicationContext.java
    
    @Override
    public 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
    
    @Override
    protected 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
    @Override
    public 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服务器过程就完了

     丰极

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

    bk