SpringBoot内置Tomcat启动原理源码分析

1、获取SpringBoot内置Tomcat自动配置类:

在SpringBoot项目中引入spring-boot-starter-web依赖,就默认使用Tomcat容器,该依赖中引入spring-boot-starter-tomcat、spring-webmvc,就引入了tomtcat核心依赖和springMvc相关jar包,这样就间接地引入了tomcat。

SpringBoot内置Tomcat启动原理源码分析

在执行SpringBoot项目启动类的main()方法,启动SpringBoot项目的过程中会加载各个jar包下META-INF/spring.factories的文件,在该文件中包含着自动配置的子路径,在refresh()方法中的invokeBeanFactoryPostProcessors()中首先会对启动类上的 @SpringBootApplication 注解进行解析,最终调用 AutoConfigurationImportSelector类中的 getAutoConfigurationEntry() 加载 META-INF/spring.factories 文件中的自动配置类,得到自动配置类的全路径,其中 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration 为tomcat自动配置类。

具体加载流程见: springBoot-启动原理;

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 1、得到META-INF/spring.factories文件中配置的所有自动配置类
    List configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 移除重复的配置类
    configurations = removeDuplicates(configurations);
    // 获取需要排除的自动配置类,eg:注解属性中的exculde的配置类
    Set exclusions = getExclusions(annotationMetadata, attributes);
    // 检查需要被排除的配置类,因为有些不是自动配置类,需要抛异常
    checkExcludedClasses(configurations, exclusions);
    // 移除需要排除的配置类
    configurations.removeAll(exclusions);
    // 根据 META-INF/spring-autoconfigure-metadata.properties 中配置的规则过虑掉一部分配置类(根据@ConditionalOnXXX注解进行过滤)
    configurations = getConfigurationClassFilter().filter(configurations);
    // 获取符合条件的配置类后,触发 AutoConfigurationImportEvent 事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 将符合条件和需要排除的配置类封装进 AutoConfigurationEntry 对象中返回
    return new AutoConfigurationEntry(configurations, exclusions);
}

SpringBoot内置Tomcat启动原理源码分析

2、ServletWebServerFactoryAutoConfiguration – tomcat自动配置类分析

SpringBoot内置Tomcat启动原理源码分析

3、 创建tomcat工厂

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {

    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider connectorCustomizers,
            ObjectProvider contextCustomizers,
            ObjectProvider> protocolHandlerCustomizers) {
        // 创建生产tomcat的工厂
        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;
    }

}

4、创建tomcat容器

在SpringBoot启动过程中会调用 AbstractApplicationContext.refresh() 方法,在该方法会调用onRefresh()方法,这个方法是个模板方法,最终会交给子类实现,在使用内置tomcat的SpringBoot项目中,最终会调用 ServletWebServerApplicationContext 实现(AbstractApplicationContext是GenericWebApplicationContext,ServletWebServerApplicationContext 是GenericWebApplicationContext),最终调用ServletWebServerApplicationContext 的createWebServer()方法创建 webServer。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
protected void onRefresh() {
    // 调用 GenericWebApplicationContext类的 onRefresh() 方法,
    super.onRefresh();
    try {
        // 获取嵌入式的Servlet容器工厂(TomcatServletWebServerFactory),并通过工厂来获取Servlet容器
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
        // 获取 Servlet容器工厂
        ServletWebServerFactory factory = getWebServerFactory();
        createWebServer.tag("factory", factory.getClass().toString());
        // 通过Servlet容器工厂加载tomcat并启动tomcat
        this.webServer = factory.getWebServer(getSelfInitializer());
        createWebServer.end();
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer));
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer));
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

在 TomcatServletWebServerFactory 类中
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    // 实例化tomcat
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    // 设置tomcat临时工作目录
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // 默认使用 org.apache.coyote.http11.Http11NioProtocol 实例化Connector
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    // 给service添加Connector
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    // 关闭热部署
    tomcat.getHost().setAutoDeploy(false);
    // 配置 Engine
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    // 实例化 TomcatWebServer,将 DispatcherServlet 以及一些Filter添加到Tomcat中
    return getTomcatWebServer(tomcat);
}

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}

//TomcatWebServer类的构造方法
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;
    initialize();
}

private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            addInstanceIdToEngineName();

            Context context = findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                    // Remove service connectors so that protocol binding doesn't
                    // happen when the service is started.

                    removeServiceConnectors();
                }
            });

            // Start the server to trigger initialization listeners
            //利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布configure_start、
            // before_init、after_start的lifecycleEvent等事件给相应的监听器
            // 本方法并没有启动tomcat
            this.tomcat.start();

            // We can re-throw failure exception directly in the main thread
            rethrowDeferredStartupExceptions();

            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
            }
            catch (NamingException ex) {
                // Naming is not enabled. Continue
            }

            // Unlike Jetty, all Tomcat threads are daemon threads. We create a
            // blocking non-daemon to stop immediate shutdown
            startDaemonAwaitThread();
        }
        catch (Exception ex) {
            stopSilently();
            destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

// Tomcat类
public void start() throws LifecycleException {
    getServer();
    server.start();
}
// LifecycleBase
public final synchronized void start() throws LifecycleException {

    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {

        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }

        return;
    }

    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    try {
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.

            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.

            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.

        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

createWebServer

5、启动tomcat容器

创建webServer后在refresh()方法中的finishRefresh()完成tomcat的启动。

最终会调用 TomcatWebServer中的start()方法启动tomcat容器。

1 protected void finishRefresh() {
  2     // Clear context-level resource caches (such as ASM metadata from scanning).

  3     clearResourceCaches();
  4
  5     // Initialize lifecycle processor for this context.

  6     initLifecycleProcessor();
  7
  8     // Propagate refresh to lifecycle processor first.

  9     // 启动tomcat
 10     getLifecycleProcessor().onRefresh();
 11
 12     // Publish the final event.

 13     publishEvent(new ContextRefreshedEvent(this));
 14
 15     // Participate in LiveBeansView MBean, if active.

 16     if (!NativeDetector.inNativeImage()) {
 17         LiveBeansView.registerApplicationContext(this);
 18     }
 19 }
 20
 21 @Override
 22 public void onRefresh() {
 23     startBeans(true);
 24     this.running = true;
 25 }
 26
 27 private void startBeans(boolean autoStartupOnly) {
 28     Map lifecycleBeans = getLifecycleBeans();
 29     Map phases = new TreeMap<>();
 30
 31     lifecycleBeans.forEach((beanName, bean) -> {
 32         if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
 33             int phase = getPhase(bean);
 34             phases.computeIfAbsent(
 35                     phase,
 36                     p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
 37             ).add(beanName, bean);
 38         }
 39     });
 40     if (!phases.isEmpty()) {
 41         // 遍历启动
 42         phases.values().forEach(LifecycleGroup::start);
 43     }
 44 }
 45
 46 public void start() {
 47     if (this.members.isEmpty()) {
 48         return;
 49     }
 50     if (logger.isDebugEnabled()) {
 51         logger.debug("Starting beans in phase " + this.phase);
 52     }
 53     Collections.sort(this.members);
 54     for (LifecycleGroupMember member : this.members) {
 55         doStart(this.lifecycleBeans, member.name, this.autoStartupOnly);
 56     }
 57 }
 58
 59 private void doStart(Mapextends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {
 60     Lifecycle bean = lifecycleBeans.remove(beanName);
 61     if (bean != null && bean != this) {
 62         String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
 63         for (String dependency : dependenciesForBean) {
 64             doStart(lifecycleBeans, dependency, autoStartupOnly);
 65         }
 66         if (!bean.isRunning() &&
 67                 (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) {
 68             if (logger.isTraceEnabled()) {
 69                 logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]");
 70             }
 71             try {
 72                 bean.start();
 73             }
 74             catch (Throwable ex) {
 75                 throw new ApplicationContextException("Failed to start bean '" + beanName + "'", ex);
 76             }
 77             if (logger.isDebugEnabled()) {
 78                 logger.debug("Successfully started bean '" + beanName + "'");
 79             }
 80         }
 81     }
 82 }
 83
 84 //WebServerStartStopLifecycle 中start() 方法的实现
 85 public void start() {
 86     this.weServerManager.start();
 87     this.running = true;
 88 }
 89
 90 void start() {
 91     this.handler.initializeHandler();
 92     this.webServer.start();
 93     this.applicationContext
 94             .publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
 95 }
 96 // 完成对tomcat的启动
 97 @Override
 98 public void start() throws WebServerException {
 99     synchronized (this.monitor) {
100         if (this.started) {
101             return;
102         }
103         try {
104             addPreviouslyRemovedConnectors();
105             Connector connector = this.tomcat.getConnector();
106             if (connector != null && this.autoStart) {
107                 performDeferredLoadOnStartup();
108             }
109             checkThatConnectorsHaveStarted();
110             this.started = true;
111             logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
112                     + getContextPath() + "'");
113         }
114         catch (ConnectorStartFailedException ex) {
115             stopSilently();
116             throw ex;
117         }
118         catch (Exception ex) {
119             PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
120             throw new WebServerException("Unable to start embedded Tomcat server", ex);
121         }
122         finally {
123             Context context = findContext();
124             ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
125         }
126     }
127 }
128
129 finishRefresh

Original: https://www.cnblogs.com/sunnyLee/p/15921395.html
Author: lee丶牧羊人
Title: SpringBoot内置Tomcat启动原理源码分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/580976/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 虚拟机最小化安装centos7.5后,如何配置centos的固定ip

    目前宿主机:只能动态获取ip 虚拟机配置为NAT模式: 查看虚拟机编辑->虚拟网络编辑器: 查看宿主机 VMnet8的状态详细信息: 下面进入配置: 首先设置centos的固…

    Java 2023年5月30日
    087
  • 【Java分享客栈】超简洁SpringBoot使用AOP统一日志管理-纯干货干到便秘

    前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧。 实现 本篇AOP统一…

    Java 2023年6月9日
    088
  • redis删除缓存时遇到的问题

    一、redis查询key的方式 redis常用两种方式用于key的精确/模糊匹配 1. KEYS pattern keys pattern用于匹配pattern所有key,会返回当…

    Java 2023年6月13日
    0101
  • Java你可能不知道的事(3)HashMap

    概述 HashMap对于做Java的小伙伴来说太熟悉了。估计你们每天都在使用它。它为什么叫做HashMap?它的内部是怎么实现的呢?为什么我们使用的时候很多情况都是用String作…

    Java 2023年6月13日
    047
  • 设计模式–Bulider模式

    &#x8D77;&#x56E0;:最近在做统计计算,创建的实体中属性比较多,都是一些数值,一开始是通过get、set方法进行赋值,占用了很多业务代码方法的长度,可读…

    Java 2023年6月5日
    076
  • 理解oauth2.0【转载】

    原文出处: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html OAuth是一个关于授权(authorization)的开放…

    Java 2023年6月16日
    062
  • C# 线程手册 第七章 网络和线程

    在本书的之前章节,我们已经深入地了解了C#.NET 中的线程并探讨了多线程编程中的不同概念和技术。现在你已经是一个线程专家啦,我们将要使用C#实现一个简单的多线程客户端-服务端程序…

    Java 2023年5月29日
    0139
  • Java高并发28-ThreadPoolExecutor原理剖析(2)

    线程池转换状态如下: Running->Shutdown 显示调用shutdown()或隐式调用finalize()中的shutdown() Running或者Shutdow…

    Java 2023年6月13日
    050
  • 你的团队工作量饱和吗?

    参与软件开发的相关人员,虽然称为工程技术人员,但本质上其实就是手艺人。手艺嘛肯定是一技之长,里面多少有些门道,外行人做不了, 也很难完全搞清楚其中的门道,合作过程中就怕被坑上当 :…

    Java 2023年6月15日
    088
  • 惰性初始化

    在上面两个类定义的方法中,有一个很特殊:toString()。每一个非基本类型的对象都有一个toString()方法,而且当编译器需要一个String而你却只有一个对象时,该方法便…

    Java 2023年6月13日
    069
  • java使用POI操作XWPFDocument 生成Word实战(一)

    注:我使用的word 2016功能简介:(1)使用jsoup解析html得到我用来生成word的文本(这个你们可以忽略)(2)生成word、设置页边距、设置页脚(页码),设置页码(…

    Java 2023年6月7日
    087
  • LeetCode随缘刷题之截断句子

    这道题相对比较简单。正好最近学到StringBuilder就用了。 package leetcode.day_12_06; public class TruncateSentenc…

    Java 2023年6月7日
    066
  • 阿里巴巴编码规范-考试认证

    阿里巴巴编码规范-考试认证 雨打梨花深闭门,忘了青春,误了青春。 1、注册阿里云账号 2、购买认证 需要怒支付一顿早餐Q,可以用支付宝支付,选择支付宝支付然后直接输入支付密码就OK…

    Java 2023年6月5日
    089
  • IO多路复用

    划分内核态/用户态 之前说过七层/五层/四层的 &#x7F51;&#x7EDC;&#x6A21;&#x578B;,我们从网络模型可以看出 &…

    Java 2023年6月7日
    082
  • 一致性 hash 环

    一致性 hash 环 最近做项目 做了一个分发器 ,需要 根据请求携带的参数 把请求分发到 不同的服务器上面,最终我选择使用 一致性hash 环 来实现 ,本篇 就主要讲解一下 一…

    Java 2023年6月9日
    076
  • XWPFDocument创建和读取Office Word文档基础篇(一)

    注:有不正确的地方还望大神能够指出,抱拳了 老铁! 建议大家使用office word来创建文档。(wps和word结构有些不一样) IBodyElement —&#8…

    Java 2023年6月7日
    096
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球