解析server.xml
- Catalina catalina = new Catalina(); // 没做其他事情
- catalina.setAwait(true);
- 以下步骤是解析servler.xml
- StandardServer server = new StandardServer(); // 没做其他事情
- catalina.setServer(server);
- server.addLifecycleListener(…);
- StandardService service = new StandardService(); // 没做其他事情
- server.addService(service);
- Connector connector = new Connector(); // 会根据配置初始化protocolHandler
- endpoint = new JIoEndpoint(); // 初始化Endpoint, JioEndpoint中会setMaxConnections(0);
- cHandler = new Http11ConnectionHandler(this); //
- ((JIoEndpoint) endpoint).setHandler(cHandler); // endpoint对应的连接处理器
- service.addConnector(connector);
- Engine engine = new StandardEngine(); // pipeline.setBasic(new StandardEngineValve());
- service.setContainer(engine);
- Host host = new StandardHost(); // pipeline.setBasic(new StandardHostValve());
- engine.addChild(host);
- Context context = new StandardContext(); // pipeline.setBasic(new StandardContextValve());
- host.addChild(context);
- engine.setParentClassLoader(Catalina.class.getClassLoader()); // 实际调用的是ContainerBase.setParentClassLoader方法,设置属性parentClassLoader为shareClassLoader
- server.setCatalina(catalina);
- server.init(); // 开始初始化
- catalina.start(); // 开始启动
总结
解析server.xml最主要的作用就是
- 把server.xml中定义的节点都生成对应的java对象,比如在解析某一个Host节点时就会对应生成一个StandardHost对象
- 把server.xml中定义的节点的层级关系解析出来,比如StandardContext对象.addChild(StandardHost对象)
- 设置每个容器的pipeline的基础Valve
初始化
Tomcat初始化主要做了以下事情:
- 将StandardServer实例注册到JMX
- 将StringCache实例注册到JMX
- 将StandardService实例注册到JMX
- container.init(); // 对StandardEngine进行初始化
- 初始化startStopExecutor线程池,用来启动子容器的
- connector.init(); // 对Connector进行初始化
- adapter = new CoyoteAdapter(this);
- protocolHandler.setAdapter(adapter);
- protocolHandler.init(); // 初始化协议处理器
- endpoint.init(); // 初始化协议处理器对应的endpoint,默认在初始化的时候就会bind
- endpoint.bind()
- serverSocketFactory = new DefaultServerSocketFactory(this);
- serverSocket = serverSocketFactory.createSocket(getPort(), getBacklog(), getAddress());
- endpoint.bind()
- endpoint.init(); // 初始化协议处理器对应的endpoint,默认在初始化的时候就会bind
- mapperListener.init(); // 没做什么其他的
总结
初始化做得事情比较少,最重要的可能就是endpoint的bind的了
启动
- catalina.start()
- getServer().start();
- fireLifecycleEvent(CONFIGURE_START_EVENT, null);
- services[i].start();
- container.start(); // 启动StandardEngine
- results.add(startStopExecutor.submit(new StartChild(children[i]))); // 每个Childrean容器(StandardHost)用单独的线程启动
- results.add(startStopExecutor.submit(new StartChild(children[i]))); // 每个Childrean容器(StandardContext)用单独的线程启动
- 以下为一个应用的启动过程
- 生成一个WebappLoader
- 启动WebappLoader
- 生成WebappClassLoader
- 将/WEB-INF/classes和/WEB-INF/lib目录作为loaderRepositories,后面应用如果加载类就从这两个目录加载
- fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
- 解析web.xml文件
- 创建WebXml对象
- 解析web.xml文件内容设置WebXml对象属性
- WebXML对象有以下几个主要属性
- Map
servlets - Map
servletMappings - Map
filters - Set
filterMaps
- 收集ServletContainerInitializers
- 将WebXML对象中的信息配置到Context对象中
- context.addFilterDef(filter);
- context.addFilterMap(filterMap);
- context.addApplicationListener(listener);
- 遍历每个ServletDef,生成一个Wrapper,context.addChild(wrapper);
- 解析web.xml文件
- 调用ServletContainerInitializers
- 上面会启动在server.xml中定义的Context,接下来会启动webapp文件夹下面的Context,是通过HostConfig触发的,调用HostConfig的start()
- deployApps();
- deployDescriptors(configBase, configBase.list()); // 描述符部署
- deployWARs(appBase, filteredAppPaths); // war包部署
- deployDirectories(appBase, filteredAppPaths); // 文件夹部署
- 生成Context对象
- context.setName(cn.getName());
- context.setPath(cn.getPath());
- host.addChild(context); // 这里会启动context,启动Context就会执行和上面类似的步骤
- deployApps();
- results.add(startStopExecutor.submit(new StartChild(children[i]))); // 每个Childrean容器(StandardContext)用单独的线程启动
- threadStart(); // 启动一个background线程
- results.add(startStopExecutor.submit(new StartChild(children[i]))); // 每个Childrean容器(StandardHost)用单独的线程启动
- executor.start(); // 启动线程池, 如果用的默认连接池,这里不会启动
- connector.start(); // 启动请求连接器
- protocolHandler.start(); // 启动接收连接
- endpoint.start(); // 启动Endpoint
- 如果没有配置Executor,就创建一个默认的Executor
- 初始化connectionLimitLatch
- 如果是NIO,则运行Poller线程
- 运行Acceptor线程
- endpoint.start(); // 启动Endpoint
- mapperListener.start();
- 主要初始化Mapper对象,Mapper对象的结构层级如下
- Mapper中有属性Host[] hosts
- Host中有属性ContextList contextList
- ContextList中有属性Context[] contexts
- Context中有属性ContextVersion[] versions
- ContextVersion中有如下属性
- Wrapper[] exactWrappers,保存需要根据Servlet名字精确匹配的Wrapper
- Wrapper[] wildcardWrappers,保存需要根据Servlet名字匹配以(“/*”)结尾的Wrapper
- Wrapper[] extensionWrappers,保存需要根据Servlet名字匹配以(“*.”)开始的Wrapper
- Wrapper中有如下两个属性
- name,Wrapper的名字
- object,真实的Wrapper的对象
- 主要初始化Mapper对象,Mapper对象的结构层级如下
- protocolHandler.start(); // 启动接收连接
- container.start(); // 启动StandardEngine
- catalina.await(); // 使用ServerSocket来监听shutdown命令来阻塞
- catalina.stop(); // 如果阻塞被解开,那么开始停止流程
总结
启动做的事情就比较多了,主要分为以下几大步骤
启动容器
启动容器主要是部署应用,部署应用分为两部分:
- 部署server.xml中定义的Context
- 部署webapp文件夹下的Context
部署一个应用主要分为以下步骤
- 生成Context对象,server.xml中定义的Context在解析server.xml时就已经生成了,webapp文件夹下的是在部署之前生成的
- 为每个应用生成一个WebappClassLoader
- 解析web.xml
- 设置Context对象中的属性,比如有哪些Wrapper
启动Connector
主要是:
- 启动Endpoint开始接收请求
- 构造Mapper对象,用来处理请求时,快速解析出当前请求对应哪个Context,哪个Wrapper