前面那一篇感觉上来的有点突兀,还是应该按照架构去慢慢解析,所以这里回归下我们整体的 Tomcat 架构,这里我们通过一个 Tomcat 的配置文件来看看
1 2 3 4 5 6 7 8 9 10 11 <Server > <Service > <Connector /> <Connector /> <Engine > <Host > <Context /> </Host > </Engine > </Service > </Server >
上次我们讲解了 connector,也提到了初始化的流程,在org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
代码中我们就能略窥一斑,
1 2 3 4 5 6 7 8 9 10 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);
这一行 具体来看下
1 2 3 public Service getService () { return getServer().findServices()[0 ]; }
又调用了,org.apache.catalina.startup.Tomcat#getServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public Server getServer () { if (server != null ) { return server; } System.setProperty("catalina.useNaming" , "false" ); server = new StandardServer (); initBaseDir(); 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 负责处理真正的逻辑,
1 2 3 4 5 6 7 8 9 10 11 12 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,也就是可以处理多个虚拟主机的业务逻辑,
1 2 3 4 5 6 7 8 9 10 11 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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
,这一点比较特殊,希望这样会有个一个大致的概念。