Tomcat 系列篇二-介绍下整体架构

前面那一篇感觉上来的有点突兀,还是应该按照架构去慢慢解析,所以这里回归下我们整体的 Tomcat 架构,这里我们通过一个 Tomcat 的配置文件来看看

<Server>
    <Service>
        <Connector />
        <Connector />
        <Engine>
            <Host>
                <Context />
            </Host>
        </Engine>
    </Service>
</Server>

上次我们讲解了 connector,也提到了初始化的流程,在
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
代码中我们就能略窥一斑,

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());

这里的 connector 是在 service 中的,而
tomcat.getService().addConnector(connector); 这一行
具体来看下

public Service getService() {
        return getServer().findServices()[0];
    }

又调用了,org.apache.catalina.startup.Tomcat#getServer

public Server getServer() {

        if (server != null) {
            return server;
        }

        System.setProperty("catalina.useNaming", "false");

        server = new StandardServer();

        initBaseDir();

        // Set configuration source
        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));

        server.setPort( -1 );

        Service service = new StandardService();
        service.setName("Tomcat");
        server.addService(service);
        return server;
    }

可以看到,先 new 了 StandardServer,再 new 了 StandardService,可以理解为创建 Server 后具体是由 service 进行服务,
而在 service 中就是上面的配置文件里显示的,service 包含了 connector,可以是多个connector,负责接入,具体内容可以参看上一篇
而后是 Engine,Engine 的关系是一个 Service 有一个 Engine,Engine 负责处理真正的逻辑,

public Engine getEngine() {
        Service service = getServer().findServices()[0];
        if (service.getContainer() != null) {
            return service.getContainer();
        }
        Engine engine = new StandardEngine();
        engine.setName( "Tomcat" );
        engine.setDefaultHost(hostname);
        engine.setRealm(createDefaultRealm());
        service.setContainer(engine);
        return engine;
    }

Engine 一般默认我们初始化的都是 StandardEngine,包括前面的 StandardServer 和StandardService,
而对于 host 来说,Engine 中可以包含多个 host,也就是可以处理多个虚拟主机的业务逻辑,

public Host getHost() {
        Engine engine = getEngine();
        if (engine.findChildren().length > 0) {
            return (Host) engine.findChildren()[0];
        }

        Host host = new StandardHost();
        host.setName(hostname);
        getEngine().addChild(host);
        return host;
    }

tomcat的特点也都是常规的懒加载,在 get 的第一次请求里进行初始化,这边同样创建了 StandardHost,对于可以有多个的 host,在 Engine 中添加也变成了addChild,而对于常规的 Tomcat 来说,往下一层就是 context 了,这个可以支持多个 web 应用,所以也是可以添加多个 context,但我这边以 springboot 嵌入的 Tomcat 举例,他是内嵌的 context

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
        File documentRoot = this.getValidDocumentRoot();
        TomcatEmbeddedContext context = new TomcatEmbeddedContext();
        if (documentRoot != null) {
            context.setResources(new LoaderHidingResourceRoot(context));
        }

        context.setName(this.getContextPath());
        context.setDisplayName(this.getDisplayName());
        context.setPath(this.getContextPath());
        File docBase = documentRoot != null ? documentRoot : this.createTempDir("tomcat-docbase");
        context.setDocBase(docBase.getAbsolutePath());
        context.addLifecycleListener(new Tomcat.FixContextListener());
        context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader());
        this.resetDefaultLocaleMapping(context);
        this.addLocaleMappings(context);

        try {
            context.setCreateUploadTargets(true);
        } catch (NoSuchMethodError var8) {
        }

        this.configureTldPatterns(context);
        WebappLoader loader = new WebappLoader();
        loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
        loader.setDelegate(true);
        context.setLoader(loader);
        if (this.isRegisterDefaultServlet()) {
            this.addDefaultServlet(context);
        }

        if (this.shouldRegisterJspServlet()) {
            this.addJspServlet(context);
            this.addJasperInitializer(context);
        }

        context.addLifecycleListener(new StaticResourceConfigurer(context));
        ServletContextInitializer[] initializersToUse = this.mergeInitializers(initializers);
        host.addChild(context);
        this.configureContext(context, initializersToUse);
        this.postProcessContext(context);
    }

是一个 TomcatEmbeddedContext,这一点比较特殊,希望这样会有个一个大致的概念。