SpringBoot(3) Tomcat的实例化与启动

906 阅读4分钟

应用上下文ApplicationContext

环境准备完毕后是打印Banner,然后创建上下文。因为是Servlet环境,这里返回AnnotationConfigServletWebServerApplicationContext,它就是应用上下文,其中有个成员变量beanFactoryDefaultListableBeanFactory的对象引用,它是默认的IoC容器。

SpringApplication->run():
    prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
SpringApplication->prepareContext():
    // 遍历initializers,执行应用上下文初始化类的initialize方法
    applyInitializers(context);
    // 触发监听器调用应用上下文初始化完毕(ApplicationContextInitializedEvent)事件
    listeners.contextPrepared(context);
    // 打印启动信息及配置文件信息
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    ......
    // 将构造方法参数(primarySources成员变量)解析,如果是bean的话添加至bdn和bdm,有多个则按照顺序添加
    load(context, sources.toArray(new Object[0]));
    // 先将SpringBoot中的监听器赋给应用上下文,触发监听器调用应用准备完毕(ApplicationPreparedEvent)事件
    listeners.contextLoaded(context);

Tomcat的实例化

AbstractApplicationContext->refresh():
    // 初始化容器中的特殊bean
    onRefresh();
ServletWebServerApplicationContext->onRefresh():
    // 创建tomcat容器
    createWebServer();
ServletWebServerApplicationContext->createWebServer():
    // 第一次进来webServer和servletContext都为空,所以走第一个if方法
    ServletWebServerFactory factory = getWebServerFactory(); 
    // this指的是AnnotationConfigServletWebServerApplicationContext
    this.webServer = factory.getWebServer(getSelfInitializer());
    initPropertySources();

getWebServerFactory()注入ServletWebServerFactory子类对象,由ServletWebServerFactoryAutoConfiguration类引入,最终实例化TomcatServletWebServerFactory对象,可以看到该类下除了Tomcat还有Jetty和Undertow,之所以没有添加到容器,是@ConditionalOnClass和@ConditionalOnMissingBean这两个注解起到了作用。

image.png

// 终于在这里看到了Tomcat的踪迹
TomcatServletWebServerFactory->getWebServer():
    // 初始化Tomcat对象
    Tomcat tomcat = new Tomcat();
    // 设置tomcat目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory
            : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // 实例化connector组件,内部实例化Http11NioProtocol,Http11NioProtocol内部实例化Endpoint
    Connector connector = new Connector(this.protocol);
    // 实例化server以及service组件,之后server添加service,service添加connector
    tomcat.getService().addConnector(connector);
    // 自定义的属性值,如端口号
    customizeConnector(connector);
    tomcat.setConnector(connector);
    // 实例化engine组件,service添加engine,实例化host组件,之后engine添加host
    tomcat.getHost().setAutoDeploy(false);
    ......
    // 实例化context组件,之后实例化wrapper组件,之后context添加wrapper,host添加context
    // 至此,Tomcat四大组件完成
    prepareContext(tomcat.getHost(), initializers);
    // 启动tomcat服务
    return getTomcatWebServer(tomcat);

用户配置文件中server开头的属性会被注入到ServerProperties类成员变量,它用于自定义Tomcat的属性值。

Tomcat的结构如图所示,其中server是Tomcat类的一个成员变量属性。host、context、wrapper是各组件的children成员变量。

C2风格体系结构 (1).png

Tomcat的启动

每个组件都是抽象类ContainerBase的子类,ContainerBase又是抽象类LifecycleMBeanBase的子类,LifecycleMBeanBase又是LifecycleBase的子类。

LifecycleBase->start():如果生命周期状态是NEW,调用init()方法,生命周期状态置为STARTING_PREP,调用startInternal()方法,生命周期状态置为STARTED(没出错的情况下)
LifecycleMBeanBase->initInternal():注册当前对象到JMX
ContainerBase->initInternal():注册一个startStop线程池
ContainerBase->startInternal():将子容器的启动放入startStop线程池中,等待其完成之后,启动背景线程。
LifecycleBase->fireLifecycleEvent():使监听器执行生命周期状态改变的事件

server、service、engine的启动

Tomcat的启动主要是各组件的启动,生命周期的变化贯穿其中。

TomcatWebServer->initialize():
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    ......
    this.tomcat.start();
Tomcat->start():
    server.start();

大致过程如下图所示,每个组件的生命周期独立。这里的组件2是组件1的最后一个组件,组件3是组件2的最后一个组件,以此类推。

未命名文件 (4).png

  • initInternal中: 在connector的initInternal()中,实例化了CoyoteAdapter

  • startInternal中: 从engine开始,子组件采用异步线程的形式启动。

service调用mapperListener.start(),初始化路由映射器Mapper类的相关属性。

service遍历connectors对象,会发现两者已经解绑了。

host、context、wrapper的启动

由于engine对应多个host,host对应多个context,所以它们的子组件采用异步线程的形式启动。

ContainerBase->startInternal():
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

// StartChild实现Callable接口
StartChild ->call():
    child.start();
  • startInternal中: context调用child.start(),wrapper的启动又用回了同步的形式。

在context的startInternal()中,实例化了StandardManager

context的setManager(contextManager),选择了一种sessionId的生成方式

context的onStartup(entry.getValue(),getServletContext()),遍历其成员变量initializers执行onStartup()方法 // --1

context调用super.threadStart()启动背景线程,背景线程执行backgroundProcess()方法,判断session是否过期。

context的setState(LifecycleState.STARTING),将service和connectors解绑,关于解绑的代码和后续可以点击此处

servlet、filter的查找与添加

// --1 最终进入以下方法
ServletWebServerApplicationContext->selfInitialize():
    // 作用域新增application
    registerApplicationScope(servletContext);
    // getServletContextInitializerBeans()返回框架以及用户自定义的servlet和filter子类
    // context添加servlet和filter
    // 如果是servlet,在children中体现,仍会调用child.start()启动wrapper
    // 如果是filter,在filterDefs与filterMaps中体现
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
ServletWebServerApplicationContext->getServletContextInitializerBeans():
    return new ServletContextInitializerBeans(getBeanFactory());
ServletContextInitializerBeans->ServletContextInitializerBeans():
    this.initializerTypes = Collections.singletonList(ServletContextInitializer.class);
    // 从IoC容器中找到实现ServletContextInitializer接口的类,将它们的泛型归类,一个是Servlet的实现类,一个是Filter的实现类
    addServletContextInitializerBeans(beanFactory);
    addAdaptableBeans(beanFactory);
    // 按照从小到大的顺序排序,如果是Ordered接口子类,取其getOrder()方法,否则取@Order或@Priority属性值,否则默认整型最大值
    List<ServletContextInitializer> sortedInitializers = this.initializers.values()
            .stream()
            .flatMap((value) -> value.stream()
                .sorted(AnnotationAwareOrderComparator.INSTANCE))
                .collect(Collectors.toList());
ServletContextInitializerBeans->addAdaptableBeans():
    // 添加IoC容器中实现Servlet接口的类
    addAsRegistrationBean(beanFactory, Servlet.class,
            new ServletRegistrationBeanAdapter(multipartConfig));
    // 添加IoC容器中实现Filter接口的类
    addAsRegistrationBean(beanFactory, Filter.class,
            new FilterRegistrationBeanAdapter());

context启动完毕,层层回溯,最终,Tomcat启动完毕。

因为没有启动connector,所以此时是不能接收请求的,关于Tomcat接收请求的时机和代码请此处