【SpringBoot系列】Spring Boot 中tomcat的启动

🤵‍♂️ 个人主页:@香菜的个人主页,加 ischongxin ,备注csdn

✍🏻作者简介:csdn 认证博客专家,游戏开发领域优质创作者,华为云享专家,2021年度华为云年度十佳博主

🐋 希望大家多多支持,我们一起进步!😄

如果文章对你有帮助的话,

欢迎评论 💬点赞👍🏻 收藏 📂加关注+

系列文章:[Spring Boot学习大纲,可以留言自己想了解的技术点

前言

好久没写了,今天继续,虽然类似的文章在网上也能搜到,但是还是自己记录下,也是实际的去探索下,说一下自己的方法

1、源码探索方法

因为网上有很多类似的文章,所以在自己探索之前肯定是先搜索一番,踩着前人的肩膀更容易一些,大概知道流程是哪些,我们只需要深入细节就好。当然还有一些人上来直接看源码,也不是不可以,只是少了一些指导,会略微费劲一些。

今天我的方法就是直接看别人的文章,然后找到核心的调试点,通过断点去看整个流程,去探索一些细节。

找到了核心类和核心方法,随便创建一个web项目,就可以开心的调试了。

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

2、探索细节

2.1 web类型的定义

上面堆栈的第4个,也就是run:307 堆栈,看到如下代码

org.springframework.boot.SpringApplication#createApplicationContext

kotlin 复制代码
protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }

这里可以看到根据不同的webApplicationType 会创建不同的web服务器

这里的问题是webApplicationType 是怎么赋值的呐?

直接搜索,可以看到构造函数的时候进行赋值的

this.webApplicationType = WebApplicationType.deduceFromClasspath();

ini 复制代码
 
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

点进去看实现,是一个静态的函数,是根据当前路径下的类判断,返回是REACTIVE 还是Servlet

csharp 复制代码
    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;
 
            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }
 
            return SERVLET;
        }
    }

2.2 Tomcat和Jetty服务器是怎么判断的

看核心代码

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer

kotlin 复制代码
private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            ServletWebServerFactory factory = this.getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
            createWebServer.end();
            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 var5) {
                throw new ApplicationContextException("Cannot initialize servlet context", var5);
            }
        }
 
        this.initPropertySources();
    }

ServletWebServerFactory factory = this.getWebServerFactory();

继续往下跟

ServletWebServerFactory接口有多个实现类,下面标红的3个就是常用的三个web容器(tomcat、jetty,undertom),启动tomcat用的是TomcatServletWebServerFactory

2.3 tomcat的工作目录是怎么初始化的

org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer

scss 复制代码
    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());
        Iterator var4 = this.serverLifecycleListeners.iterator();
 
        while(var4.hasNext()) {
            LifecycleListener listener = (LifecycleListener)var4.next();
            tomcat.getServer().addLifecycleListener(listener);
        }
 
        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 var8 = this.additionalTomcatConnectors.iterator();
 
        while(var8.hasNext()) {
            Connector additionalConnector = (Connector)var8.next();
            tomcat.getService().addConnector(additionalConnector);
        }
 
        this.prepareContext(tomcat.getHost(), initializers);
        return this.getTomcatWebServer(tomcat);
    }

这里可以看到tomcat的相关设置。

首先说一下这个 Connector 连接器,Tomcat 有两个核心功能:

处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。 加载和管理 Servlet,以及具体处理 Request 请求。 针对这两个功能,Tomcat 设计了两个核心组件来分别完成这两件事,即:连接器(Connector)和容器(Container)。

整个过程大致就是:Connector 连接器接收连接请求,创建Request和Response对象用于和请求端交换数据,然后分配线程让Engine(也就是Servlet容器)来处理这个请求,并把产生的Request和Response对象传给Engine。当Engine处理完请求后,也会通过Connector将响应返回给客户端。

这里面提到了 Engine,这个是 Tomcat 容器里的顶级容器(Container),我们可以通过 Container 类查看其他的子容器:Engine、Host、Context、Wrapper

3、总结

Tomcat 的启动主要是实例化两个组件:Connector、Container。

Spring Boot 创建 Tomcat 时,会先创建一个根上下文,将 WebApplicationContext 传给 Tomcat;

启动 Web 容器,需要调用 getWebserver(),因为默认的 Web 环境就是 TomcatServletWebServerFactory,所以会创建 Tomcat 的 Webserver,这里会把根上下文作为参数给 TomcatServletWebServerFactory 的 getWebServer();

启动 Tomcat,调用 Tomcat 中 Host、Engine 的启动方法。

相关推荐
NiNg_1_2341 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer084 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml45 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠6 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#