Springboot内嵌Tomcat介绍

在SpringBoot出现之前,我们部署一个java应用的方式如下图所示
在这里插入图片描述

而SpringBoot出现之后,内嵌了Tomcat、Jetty这样的Servlet容器,即无需再将应用打包成war部署,将应用打包成jar,直接运行一个jar包就能启动一个web服务。

实现原理

Tomcat-embed

Springboot能够将Tomcat内嵌,是因为Tomcat提供了一套JavaAPI,能够通过Tomcat tomcat = new Tomcat()来创建一个Tomcat容器。只需要引入Maven依赖
在这里插入图片描述

Springboot源码解读

任意一个Springboot应用,都有一个main()函数作为应用的启动方法,里面调用了SpringApplication.run(MyApplication.class, args)

public ConfigurableApplicationContext run(String... args) {
    ConfigurableApplicationContext context = null;
    // 创建spring容器对象 ApplicationContext
    context = createApplicationContext();
    // 做一些初始化容器之前的预备操作
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    // 启动spring容器核心方法,包括启动tomcat
    refreshContext(context);
    // 做一些容器启动之后的操作(当前这个方法是空的)
    afterRefresh(context, applicationArguments);
    return context;
}

这里的refreshContext(context),对spring的refresh()进行了封装

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 在容器启动之前做一些准备操作
        prepareRefresh();
        // 通知子类去刷新实例工厂
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 初始化当前容器中的实例工厂
        prepareBeanFactory(beanFactory);
        try {
            // 允许容器中的基类对实例工厂进行后置处理
            postProcessBeanFactory(beanFactory);
            // 调用实例工厂中的后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // 注册实例的后置处理器,在创建实例时进行拦截
            registerBeanPostProcessors(beanFactory);
            // 初始化消息资源
            initMessageSource();
            // 初始化容器中的事件处理器
            initApplicationEventMulticaster();
            // 初始化一些在特定容器中特殊的实例
            onRefresh();
            // 检查监听器的实例并注册它们
            registerListeners();
            // 实例化所有非懒加载的实例
            finishBeanFactoryInitialization(beanFactory);
            // 最后一步:发布一些响应事件
            finishRefresh();
        }
        catch (BeansException ex) {
        }
        finally {
        }
    }
}

这个方法调用完,spring容器就基本完成了初始化过程,tomcat也是在这个方法内部完成的创建

创建WebServer容器

Springboot内嵌的各种web容器实例,都是在onRefresh()中进行创建的

protected void onRefresh() throws BeansException {
    // For subclasses: do nothing by default.
}
@Override
protected void onRefresh() {
    super.onRefresh();
    createWebServer();
}

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        // 通过工厂创建WebServer实例
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
}

创建Tomcat实例

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    // 1.创建一个tomcat临时文件路径
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // 2.创建连接协议,默认使用HTTP1.1协议,NIO网络模型
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    // 3.创建主机,并关闭热部署
    tomcat.getHost().setAutoDeploy(false);
    // 4.配置引擎
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    // 5.初始化TomcatEmbeddedContext
    prepareContext(tomcat.getHost(), initializers);
    // 6.启动tomcat并返回TomcatWebServer对象
    return getTomcatWebServer(tomcat);
}

getTomcatWebServer(tomcat)方法

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

TomcatWebServer(tomcat,autoStart)方法

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    initialize();
}

initialize()方法

private void initialize() throws WebServerException {
    synchronized (this.monitor) {
        try {
            // 给Engine命名
            addInstanceIdToEngineName();
            // 获取Host中的Context
            Context context = findContext();
            // 绑定Context的生命周期监听器
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                    removeServiceConnectors();
                }
            });
            // 启动tomcat,触发初始化监听器
            this.tomcat.start();
            // 启动过程中子线程的异常从主线程抛出
            rethrowDeferredStartupExceptions();
            // 给当前Context绑定类加载器
            ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
            // tomcat的所有线程都是守护线程,这里启动一个阻塞的非守护线程来确保tomcat能及时停止
            startDaemonAwaitThread();
        }
        catch (Exception ex) {
        }
    }
}

启动Web服务

真正完成springboot启动的方法,依然是由TomcatWebServer这个类完成的,这个类封装了控制整个web服务声明周期的方法,比如initialize(), start(), stop()等等,而这个start()显然就是web服务启动的方法了。

@Override
public void start() throws WebServerException {
    synchronized (this.monitor) {
        Connector connector = this.tomcat.getConnector();
        if (connector != null && this.autoStart) {
            performDeferredLoadOnStartup();
        }
        logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
                + getContextPath() + "'");
    }
}

总结

Spring框架基于接口的设计模式,使得整个框架具有良好的扩展性。
首先,通过继承AbstractApplicationContext,重写onRefresh()对web容器进行初始化,重写finishRefresh()启动web服务。
其次,spring抽象了WebServer接口,提供了“启动”和“停止”两个基本方法,具体方法由不同的web容器各自实现,其中tomcat的实现类叫TomcatWebServer。
最后,TomcatWebServer在构造方法中调用initialize()初始化tomcat,调用start()方法启动web服务,在spring容器销毁之前调用stop()完成tomcat生命周期。

参考:https://mp.weixin.qq.com/s/B0of5OtN89bAn_wSgYNqPw

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值