当前位置: 代码网 > 服务器>服务器>Tomcat > Spring boot整合tomcat底层原理剖析

Spring boot整合tomcat底层原理剖析

2024年05月19日 Tomcat 我要评论
从源码层面理解spring boot的默认web容器,以及他们是如何关联起来的。本文结论源码基于spring boot2.6.6项目的pom.xml中存在spring-boot-starter-web

从源码层面理解spring boot的默认web容器,以及他们是如何关联起来的。

本文结论

  • 源码基于spring boot2.6.6
  • 项目的pom.xml中存在spring-boot-starter-web的时候,在项目启动时候就会自动启动一个tomcat。
  • 自动配置类servletwebserverfactoryautoconfiguration找到系统中的所有web容器。我们以tomcat为主。
  • 构建tomcatservletwebserverfactory的bean。
  • springboot的启动过程中,会调用核心的refresh方法,内部会执行onrefresh()方法,onrefresh()方法是一个模板方法,他会执行会执行子类servletwebserverapplicationcontext的onrefresh()方法。
  • onrefresh()方法中调用getwebserver启动web容器。

spring-boot-starter-web内部有什么?

  • 在spring-boot-starter-web这个starter中,其实内部间接的引入了spring-boot-starter-tomcat这个starter,这个spring-boot-starter-tomcat又引入了tomcat-embed-core依赖,所以只要我们项目中依赖了spring-boot-starter-web就相当于依赖了tomcat。

自动配置类:servletwebserverfactoryautoconfiguration在spring-boot-autoconfigure-2.6.6.jar这个包中的spring.factories文件内,配置了大量的自动配置类,其中就包括自动配置tomcat的自动配置类:servletwebserverfactoryautoconfiguration

自动配置类的代码如下

// full模式
@configuration(proxybeanmethods = false)

// 配置类解析顺序
@autoconfigureorder(ordered.highest_precedence)

// 条件注解:表示项目依赖中要有servletrequest类(server api)
@conditionalonclass(servletrequest.class)
// 表示项目应用类型得是springmvc(在启动过程中获取的springboot应用类型)
@conditionalonwebapplication(type = type.servlet)

// 读取server下的配置文件
@enableconfigurationproperties(serverproperties.class)

// import具体的加载配置的类和具体web实现容器
@import({ servletwebserverfactoryautoconfiguration.beanpostprocessorsregistrar.class,
		servletwebserverfactoryconfiguration.embeddedtomcat.class,
		servletwebserverfactoryconfiguration.embeddedjetty.class,
		servletwebserverfactoryconfiguration.embeddedundertow.class })
public class servletwebserverfactoryautoconfiguration {
	......
}
  • servletrequest是存在于tomcat-embed-core-9.0.60.jar中的的一个类,所以@conditionalonclass(servletrequest.clas s)会满足。
  • spring-boot-starter-web中,间接的引入了spring-web、spring-webmvc等依赖,所以@conditionalonwebapplication(type = type.servlet)条件满足。
  • 上面的俩个条件都满足,所以spring回去解析这个配置类,在解析过程中会发现他import了三个类!我们重点关注embeddedtomcat。其他俩个的内部条件注解不满足!
@configuration(proxybeanmethods = false)
// tomcat内部的类,肯定都存在
@conditionalonclass({ servlet.class, tomcat.class, upgradeprotocol.class })

// 程序员如果自定义了servletwebserverfactory的bean,那么这个bean就不加载。
@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();
            // orderedstream()调用时会去spring容器中找到tomcatconnectorcustomizer类型的bean,默认是没有的,程序员可以自己定义。这个bean可以设置一些tomcat的配置,比如端口、协议...
            // tomcatconnectorcustomizer:是用来配置tomcat中的connector组件的
            factory.gettomcatconnectorcustomizers().addall(connectorcustomizers.orderedstream().collect(collectors.tolist()));
            // tomcatcontextcustomizer:是用来配置tomcat中的context组件的
            factory.gettomcatcontextcustomizers().addall(contextcustomizers.orderedstream().collect(collectors.tolist()));
            // tomcatprotocolhandlercustomizer:是用来配置tomcat中的protocolhandler组件的
            factory.gettomcatprotocolhandlercustomizers().addall(protocolhandlercustomizers.orderedstream().collect(collectors.tolist()));
            return factory;
        }
    }
}
  • 对于另外的embeddedjetty和embeddedundertow,逻辑类似,都是判断项目依赖中是否有jetty和undertow的依赖,如果有,那么对应在spring容器中就会存在jettyservletwebserverfactory类型的bean、或者存在undertowservletwebserverfactory类型的bean。

tomcatservletwebserverfactory的作用:获取webserver对象

  • tomcatservletwebserverfactory他实现了servletwebserverfactory这个接口。
  • servletwebserverfactory接口内部只有一个方法是获取webserver对象。

  • webserver拥有启动、停止、获取端口等方法,就会发现webserver其实指的就是tomcat、jetty、undertow。

  • 而tomcatservletwebserverfactory就是用来生成tomcat所对应的webserver对象,具体一点就是tomcatwebserver对象,并且在生成tomcatwebserver对象时会把tomcat给启动起来。
  • 在源码中,调用tomcatservletwebserverfactory对象的getwebserver()方法时就会启动tomcat。
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.setbasedir(basedir.getabsolutepath());
    for (lifecyclelistener listener : this.serverlifecyclelisteners) {
        tomcat.getserver().addlifecyclelistener(listener);
    }
    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);

    // 启动tomcat,这个方法内部有this.tomcat.start();
    return gettomcatwebserver(tomcat);
}

spring boot启动的时候启动tomcat

  • springboot的启动过程中,会调用核心的refresh方法,内部会执行onrefresh()方法,onrefresh()方法是一个模板方法,他会执行会执行子类servletwebserverapplicationcontext的onrefresh()方法。
protected void onrefresh() {
    // 模板方法,先调用它父类的,一般是空方法
    super.onrefresh();
    try {
        // 创建web容器
        createwebserver();
    }
    catch (throwable ex) {
        throw new applicationcontextexception("unable to start web server", ex);
    }
}

这个方法会调用createwebserver()方法。

// 最核心的俩行代码
private void createwebserver() {
    ......
    // 获取web容器,多个或者没有的时候报错
    servletwebserverfactory factory = getwebserverfactory();

    // 调用这个容器的getwebserver方法,上面的启动tomcat的方法!
    this.webserver = factory.getwebserver(getselfinitializer());
    ......
}
  • getwebserverfactory控制项目组有且只能有一个web容器!
protected servletwebserverfactory getwebserverfactory() {
    // use bean names so that we don't consider the hierarchy

    // 得到所有类型为servletwebserverfactory的bean。tomcatservletwebserverfactory、jettyservletwebserverfactory、undertowservletwebserverfactory都是他得到子类!
    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));
    }

    // 返回唯一的一个web容器!
    return getbeanfactory().getbean(beannames[0], servletwebserverfactory.class);
}

获取tomcat的配置

  • 自动配置类servletwebserverfactoryautoconfiguration上除了import三个web容器,还import了beanpostprocessorsregistrar。
  • beanpostprocessorsregistrar实现了importbeandefinitionregistrar,所以他会在spring启动的时候调用registerbeandefinitions方法。
  • registerbeandefinitions会注册一个bean:webserverfactorycustomizerbeanpostprocessor。
public void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry) {
    // bean工厂,一个aware回调进行赋值
    if (this.beanfactory == null) {
        return;
    }
    // 注册webserverfactorycustomizerbeanpostprocessor这个bean。
    registersyntheticbeanifmissing(registry, "webserverfactorycustomizerbeanpostprocessor",
        webserverfactorycustomizerbeanpostprocessor.class,
        webserverfactorycustomizerbeanpostprocessor::new);

    // 注册errorpageregistrarbeanpostprocessor
    registersyntheticbeanifmissing(registry, "errorpageregistrarbeanpostprocessor",
        errorpageregistrarbeanpostprocessor.class, errorpageregistrarbeanpostprocessor::new);
}
  • webserverfactorycustomizerbeanpostprocessor实现了beanpostprocessor,所以他会在启动的时候调用postprocessbeforeinitialization方法。
private void postprocessbeforeinitialization(webserverfactory webserverfactory) {
    // 找到webserverfactorycustomizer的bean
    lambdasafe.callbacks(webserverfactorycustomizer.class, getcustomizers(), webserverfactory)
        // 标记日志用的类
        .withlogger(webserverfactorycustomizerbeanpostprocessor.class)
        // 调用customize方法,传入webserverfactory
        .invoke((customizer) -> customizer.customize(webserverfactory));
}
  • postprocessbeforeinitialization中会调用webserverfactorycustomizer类customize方法,在系统中的唯一实现:servletwebserverfactorycustomizer的customize方法。
  • customize把配置中的内容设置到configurableservletwebserverfactory对象中。他的实现tomcatservletwebserverfactory在启动的时候就会有值!
@override
public void customize(configurableservletwebserverfactory factory) {
    propertymapper map = propertymapper.get().alwaysapplyingwhennonnull();
    map.from(this.serverproperties::getport).to(factory::setport);
    map.from(this.serverproperties::getaddress).to(factory::setaddress);
    map.from(this.serverproperties.getservlet()::getcontextpath).to(factory::setcontextpath);
    map.from(this.serverproperties.getservlet()::getapplicationdisplayname).to(factory::setdisplayname);
    map.from(this.serverproperties.getservlet()::isregisterdefaultservlet).to(factory::setregisterdefaultservlet);
    map.from(this.serverproperties.getservlet()::getsession).to(factory::setsession);
    map.from(this.serverproperties::getssl).to(factory::setssl);
    map.from(this.serverproperties.getservlet()::getjsp).to(factory::setjsp);
    map.from(this.serverproperties::getcompression).to(factory::setcompression);
    map.from(this.serverproperties::gethttp2).to(factory::sethttp2);
    map.from(this.serverproperties::getserverheader).to(factory::setserverheader);
    map.from(this.serverproperties.getservlet()::getcontextparameters).to(factory::setinitparameters);
    map.from(this.serverproperties.getshutdown()).to(factory::setshutdown);
    for (weblistenerregistrar registrar : this.weblistenerregistrars) {
        registrar.register(factory);
    }
    if (!collectionutils.isempty(this.cookiesamesitesuppliers)) {
        factory.setcookiesamesitesuppliers(this.cookiesamesitesuppliers);
    }
}

servletwebserverfactorycustomizer这个bean是哪里的?

  • 在我们自动配置类servletwebserverfactoryautoconfiguration中定义。
@bean
public servletwebserverfactorycustomizer servletwebserverfactorycustomizer(serverproperties serverproperties, objectprovider<weblistenerregistrar> weblistenerregistrars, objectprovider<cookiesamesitesupplier> cookiesamesitesuppliers) {
    return new servletwebserverfactorycustomizer(serverproperties,weblistenerregistrars.orderedstream().collect(collectors.tolist()),cookiesamesitesuppliers.orderedstream().collect(collectors.tolist()));
}

到此这篇关于spring boot整合tomcat底层原理的文章就介绍到这了,更多相关spring boot整合tomcat内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

  • Tomcat服务器的配置与启动全过程

    Tomcat服务器的配置与启动全过程

    tomcat的下载与安装tomcat是apache软件基金会的一个开源免费的项目 , 它是一个轻量级web服务器 , 体积小 , 运行速度快(只实现了servl... [阅读全文]
  • Tomcat Catalina为什么不new出来原理解析

    一、catalina为什么不new出来?掌握了java的类加载器和双亲委派机制,现在我们就可以回答正题上来了,tomcat的类加载器是怎么设计的?1.web容器的特性web容器有其…

    2024年05月19日 服务器
  • war包部署到Tomcat下运行的实现步骤

    war包部署到tomcat下运行1、配置jdk安装路径到环境变量2、配置tomcat安装路径到环境变量3、从eclipse导出war包在控制台console中查看war包保存的位置…

    2024年05月19日 服务器
  • Tomcat架构设计及Servlet作用规范讲解

    1.servlet规范1.1 servlet作用讲解servlet是javaee规范中的一种,主要是为了扩展java作为web服务的功能,统一定义了对应的接口,比如servlet接…

    2024年05月19日 服务器
  • Windows 下修改Tomcat jvm参数的方法

    一、设置windows服务自动启动方式修改修改注册表或者修改运行tomcatw.exe出来的“java”选项都行1、注册表修改运行:regedit找到:(6…

    2024年05月19日 服务器
  • Tomcat生命周期详解

    引言在上篇文章中我们看到了tomcat架构中的核心组件,而且各个组件都有各自的作用,各司其职,而且相互之间也有对应的父子关系,那么这些对象的创建,调用,销毁等操作是怎么处理呢?也就…

    2024年05月19日 服务器

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com