SpringBoot - 内嵌 tomcat 等 servlet 容器的原理

关于 SpringBoot 对嵌入式 Servlet 容器的支持

SpringBoot 官方文档对于嵌入式 Servlet 容器的支持 中,我们知道一下几点:

  • SpringBoot 支持内嵌的 TomcatJettyUndertow 服务,并且提供了对应的 Starter 来配置对应的实例。
  • 我们可以通过使用 Spring Bean 或者 扫描 Servlet 组件 的方式根据 Servlet 规范来注册 servletsfilterslisteners

在 Web 项目中,我们会引入 spring-boot-starter-web 这个 starter 来开启对 Web 的支持:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

在 pom.xml 文件中可以看到,SpringBoot 默认使用 Tomcat 作为 Web Servlet 容器:

在这里插入图片描述

SpringBoot 如何集成嵌入式 Tomcat 的分析

先从程序入口 run 方法开始看,这里只分析 Tomcat 的启动流程,关于 SpringBoot 其他组件的加载和初始化配置 请查看 SpringBoot 学习 专栏:

在这里插入图片描述

public ConfigurableApplicationContext run(String... args) {
   ...
    try {
        ...
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 核心流程,刷新 spring 上下文
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        ...
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, listeners);
        throw new IllegalStateException(var9);
    }
    ...
}

进入 refreshContext(context):

private void refreshContext(ConfigurableApplicationContext context) {
  	if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }
	// 核心流程
    this.refresh((ApplicationContext)context);
}

进入 refresh((ApplicationContext)context),看到在 SpringBoot 2.3.12.RELEASE 版本中这个方法加上了 @Deprecated 注解,表示在后面的版本中会替换掉这个方法

@Deprecated
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
    this.refresh((ConfigurableApplicationContext)applicationContext);
}

protected void refresh(ConfigurableApplicationContext applicationContext) {
 	applicationContext.refresh();
}

进入到 ConfigurableApplicationContextrefresh() 方法,有三个实现类,我们是 servlet 环境,所以看实现类 ServletWebServerApplicationContext

3个实现类

public final void refresh() throws BeansException, IllegalStateException {
 	try {
 		// 调用父类(AbstractApplicationContext)的 refresh 方法
        super.refresh();
    } catch (RuntimeException var3) {
        WebServer webServer = this.webServer;
        if (webServer != null) {
            webServer.stop();
        }

        throw var3;
    }
}

进入父类(AbstractApplicationContext)的 refresh 方法:

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            ...
            // 核心流程
            this.onRefresh();
            ...
        } catch (BeansException var9) {
            ...
        } finally {
            this.resetCommonCaches();
        }

    }
}

onRefresh() 方法由实现类 ServletWebServerApplicationContext 提供:

在这里插入图片描述

protected void onRefresh() {
    super.onRefresh();

    try {
    	// 核心流程,创建 web 容器
        this.createWebServer();
    } catch (Throwable var2) {
        throw new ApplicationContextException("Unable to start web server", var2);
    }
}
private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = this.getServletContext();
    if (webServer == null && servletContext == null) {
    	// 工厂模式,获取 IoC 容器中 ServletWebServerFactory 类型的 Bean,默认使用 TomcatServletWebServerFactory
        ServletWebServerFactory factory = this.getWebServerFactory();
        // 核心流程
        this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
        this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
        this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
    } else if (servletContext != null) {
        try {
            this.getSelfInitializer().onStartup(servletContext);
        } catch (ServletException var4) {
            throw new ApplicationContextException("Cannot initialize servlet context", var4);
        }
    }

    this.initPropertySources();
}

进入 TomcatServletWebServerFactorygetWebServer 方法:

public WebServer getWebServer(ServletContextInitializer... initializers) {
   if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }

    Tomcat tomcat = new Tomcat();
    File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    this.customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    this.configureEngine(tomcat.getEngine());
    Iterator var5 = this.additionalTomcatConnectors.iterator();

    while(var5.hasNext()) {
        Connector additionalConnector = (Connector)var5.next();
        tomcat.getService().addConnector(additionalConnector);
    }

    this.prepareContext(tomcat.getHost(), initializers);
    // 核心流程
    return this.getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
 	return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}

调用 TomcatWebServer 的有参构造,初始化了一个 TomcatWebServer

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
  	this.monitor = new Object();
    this.serviceConnectors = new HashMap();
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
    // 核心流程
    this.initialize();
}
private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
    synchronized(this.monitor) {
        try {
            ...
            // 核心流程,启动初始化好的 tomcat 实例
            this.tomcat.start();
            this.rethrowDeferredStartupExceptions();
            ...
            this.startDaemonAwaitThread();
        } catch (Exception var6) {
            this.stopSilently();
            this.destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", var6);
        }

    }
}

进入 Tomcatstart() 方法:

@Override
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)) {
    	// 核心流程1
        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);
        // 核心流程2
        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());
    }
}

再下面就是 Tomcat 的启动流程了,以及如何监听 HTTP 请求,这里暂时不继续分析了,后面就涉及到 Tomcat 的具体实现了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值