一、tomcat源码运行
1. 下载源码
官方下载地址:http://archive.apache.org/dist/tomcat/
本次编译使用的是apache-tomcat-9.0.44-src
,下载地址:http://archive.apache.org/dist/tomcat/tomcat-9/v9.0.44/src/apache-tomcat-9.0.44-src.zip

下载源代码点击
src/
,如果不需要源码可以点击bin/
,然后根据对应的文件后缀来下载对应的文件
2. 添加pom文件
在根目录下添加pom.xml
文件

然后添加如下内容:(如何知道需要哪些依赖呢?可以先不写依赖,运行期间报缺少哪个类就加上这个类对应的依赖)
这个pom
文件版本有问题,可用复制完整pom
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat9</artifactId>
<name>tomcat-9.0.44</name>
<version>9.0.44</version>
<build>
<finalName>tomcat-9.0.44</finalName>
<sourceDirectory>java</sourceDirectory>
<!--<testSourceDirectory>test</testSourceDirectory> test 下的有些文件报错,因此将test文件夹去掉了-->
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>test</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/biz.aQute.bnd/annotation -->
<!-- https://mvnrepository.com/artifact/biz.aQute.bnd/biz.aQute.bndlib -->
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bndlib</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.jasper/org.apache.jasper -->
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>9.0.41</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-apache-log4j</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-commons-logging</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>javax.xml.rpc</groupId>
<artifactId>javax.xml.rpc-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.6.1</version>
</dependency>
</dependencies>
</project>
修改Maven
仓库,然后使用IDEA
打开该项目

3. 解决报错
报了3个找不到符号
的错误

方案一:
可以把所有VERSION_15
改为VERSION_1_8

方案二:
查看CompilerOptions
类的源码可用看到到VERSION
到14
就没有了,所以就报了找不到符号
的错误

查看同版本的不含源码的包,可用看到ecj
的版本为4.18
下载地址:http://archive.apache.org/dist/tomcat/tomcat-9/v9.0.44/bin/apache-tomcat-9.0.44.zip

查看pom
文件依赖,发现其是tomcat-jasper
的子包

到仓库看,发现最新的版本是3.31.0
,我也是服了,这不是误导人吗?

排除tomcat-jasper
里的ecj
依赖,然后再添加新的ecj
依赖,此时就成功了
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>9.0.41</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
<version>3.31.0</version>
</dependency>

4. 运行项目
1. 运行启动类
非源码运行tomcat
,在Windows
系统下运行的是startup.bat
,在linux
系统下运行的是startup.sh
由于tomcat
是由java
写的,因此肯定会有一个启动类,打开startup.bat
,可用看到call "%EXECUTABLE%" start %CMD_LINE_ARGS%
执行的就是set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"

查看catalina.bat
文件,有如下配置set MAINCLASS=org.apache.catalina.startup.Bootstrap

搜索MAINCLASS
就可以看到在最后面执行了改主类
if not "%JPDA%" == "" goto doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
%_EXECJAVA% %CATALINA_LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity
%_EXECJAVA% %CATALINA_LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
%_EXECJAVA% %CATALINA_LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda
%_EXECJAVA% %CATALINA_LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end

根据路径,然后就发现了主类

运行后报了6
个错误
B:\apache-tomcat-9.0.44-src\java\org\apache\catalina\mbeans\JmxRemoteLifecycleListener.java:610:62
java: 程序包sun.rmi.registry不存在
B:\apache-tomcat-9.0.44-src\java\org\apache\catalina\mbeans\JmxRemoteLifecycleListener.java:620:9
java: 方法不会覆盖或实现超类型的方法
B:\apache-tomcat-9.0.44-src\java\org\apache\catalina\mbeans\JmxRemoteLifecycleListener.java:625:9
java: 方法不会覆盖或实现超类型的方法
B:\apache-tomcat-9.0.44-src\java\org\apache\catalina\mbeans\JmxRemoteLifecycleListener.java:629:9
java: 方法不会覆盖或实现超类型的方法
B:\apache-tomcat-9.0.44-src\java\org\apache\catalina\mbeans\JmxRemoteLifecycleListener.java:633:9
java: 方法不会覆盖或实现超类型的方法
B:\apache-tomcat-9.0.44-src\java\org\apache\catalina\mbeans\JmxRemoteLifecycleListener.java:637:9
java: 方法不会覆盖或实现超类型的方法

2. 修改java版本
按ctrl
点击registry
,发现我用的java是17
,在File
-> Project Structure... Ctrl+ Alt+ Shift+S
的Project
里将SDK:
和Language level:
都改为1.8
版本 (这tomcat
好奇怪,install
之前我还特意检查了一下版本都是1.8
,而且我环境变量也是1.8
,都不知道他从哪检测到java 17
的)

在File
-> Settings... Ctrl+Alt+S
-> Build, Execution, Deployment
-> Compiler
-> Java Compiler
里也检查一下版本

3. 修改jar包版本
然后启动报了这个错,这个错一般就是jar包版本不对
B:\apache-tomcat-9.0.44-src\java\org\apache\jasper\compiler\JDTCompiler.java:40:37
java: 无法访问org.eclipse.jdt.core.compiler.IProblem
错误的类文件: /A:/maven/apache-maven-3.8.2/mvn_respository/org/eclipse/jdt/ecj/3.31.0/ecj-3.31.0.jar!/org/eclipse/jdt/core/compiler/IProblem.class
类文件具有错误的版本 55.0, 应为 52.0
请删除该文件或确保该文件位于正确的类路径子目录中。

查看ecj-4.18.jar
包的META-INF\MANIFEST.MF
文件,可用看到版本为3.24.0
Bundle-Version: 3.24.0.v20201123-0742

查找改版本

修改为该版本后,成功启动
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
<version>3.24.0</version>
</dependency>
不过有乱码

报了以下错误不用管
信息: è³å°æä¸ä¸ªJARè¢«æ«æç¨äºTLDä½å°æªå
å«TLDã 为æ¤è®°å½å¨å¯ç¨è°è¯æ¥å¿è®°å½ï¼ä»¥è·åå·²æ«æä½æªå¨å
¶ä¸æ¾å°TLDç宿´JARå表ã 卿«ææé´è·³è¿ä¸éè¦çJARå¯ä»¥ç¼©çå¯å¨æ¶é´åJSPç¼è¯æ¶é´ã
九月 29, 2022 8:16:53 下午 org.apache.catalina.core.StandardContext listenerStart
严重: é
ç½®åºç¨ç¨åºçå¬å¨[listeners.ContextListener]é误
java.lang.ClassNotFoundException: listeners.ContextListener
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1364)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1187)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:539)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:520)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:150)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4640)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5177)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:717)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:706)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1184)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1925)

pom
文件
完整<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat9</artifactId>
<name>tomcat-9.0.44</name>
<version>9.0.44</version>
<build>
<finalName>tomcat-9.0.44</finalName>
<sourceDirectory>java</sourceDirectory>
<!--<testSourceDirectory>test</testSourceDirectory> test 下的有些文件报错,因此将test文件夹去掉了-->
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>test</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/biz.aQute.bnd/annotation -->
<!-- https://mvnrepository.com/artifact/biz.aQute.bnd/biz.aQute.bndlib -->
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bndlib</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.jasper/org.apache.jasper -->
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>9.0.41</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
<version>3.24.0</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-apache-log4j</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-commons-logging</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>javax.xml.rpc</groupId>
<artifactId>javax.xml.rpc-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.6.1</version>
</dependency>
</dependencies>
</project>
5 修改启动参数
添加启动类的运行参数,设置字符编码
-Duser.language=en
-Duser.region=US
-Dfile.encoding=UTF-8
-Dsun.jnu.encoding=UTF-8

设置字符编码后,就没有乱码了

二、tomcat总体架构
关系:Server
-> Service
-> Connector
-> Engine
-> Host
-> Context
-> Wrapper
-> Servlet
理解server.xml
server:即服务器,每个tomcat程序启动后,就是一个server。 service:这是一种抽象的服务,通常是在日志或者管理时使用这样一个概念。它把连接器和处理引擎结合在一起。 connector:用于处理连接和并发,通常包括两种方式HTTP和AJP。HTTP是用于网页地址栏http这种访问方式;AJP一般用于搭配Apache服务器。 engine:处理引擎,所有的请求都是通过处理引擎处理的。 host:虚拟主机,用于进行请求的映射处理。每个虚拟主机可以看做独立的请求文件。 realm:用于配置安全管理角色,通常读取tomcat-uesrs.xml进行验证。 context:上下文,对应于web应用。
1. server.xml文件
要想知道tomcat
有什么,需要先看conf/server.xml
文件
Server
:首先是Server
大标签,代表整个tomcat服务器。
Listener
:里面配了几个Listener
监听器。
GlobalNamingResources
:根据注释可以知道该标签内可以配置JNDI
(Java Naming and Directory Interface,Java命名和目录接口)。可以在里面定义一些资源,比如指定数据库连接池,可以使用 @Resource 注解获取这些资源。
Service
:一个Service是一组连接器可以共享一个容器,一个Server
可以有多个Service
,每个Service
可以完成不同的功能。
这里配了一个Catalina
服务,Catalina服务[Tomcat请求处理服务],使用Connector可以接收数据,处理请求。我们也可以自定义自己的服务,完成自己想要的功能,比如操作redis
。
Connector
:其port
属性可以指定我们要监听的端口,每一个Connector
可以监听一个不同的端口,一个Service
可以有多个Connector
。
Engine
:正真的请求会交给引擎进行处理,引擎控制整个的处理逻辑。
Realm
:认证信息
Host
:主机。一个Engine
可以有多个Host
,每一个Host代表一个虚拟域名映射系统,根据Host可以进行虚拟主机隔离。name="localhost" appBase="webapps"
说明了为什么访问localhost
会在webapps
里找, unpackWARs="true"
自动解压war包,autoDeploy="true"
自动部署,一个webapp
里可以有多个web
应用。
Context
:一个web应用就是一个context
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>

Server
接口
2. Server
接口主要指定Tomcat能干什么
public interface Server extends Lifecycle {
...
/**
* @return the outer Catalina startup/shutdown component if present.
*/
public Catalina getCatalina();
/**
* Set the outer Catalina startup/shutdown component if present.
*
* @param catalina the outer Catalina component
*/
public void setCatalina(Catalina catalina);
public File getCatalinaBase();
public void setCatalinaBase(File catalinaBase);
public File getCatalinaHome();
public void setCatalinaHome(File catalinaHome);
/**
* Add a new Service to the set of defined Services.
* 添加我们自定义的服务
* @param service The Service to be added
*/
public void addService(Service service);
...
}
该接口定义了获取和修改Catalina
的方法以及其相关的方法。

当然其也有addService
方法,添加我们自定义的服务

Service
接口
3. Service
接口指定该功能怎么做
public interface Service extends Lifecycle {
/** TODO `Catalina`里的`Service`是处理请求的服务,真正会交给引擎进行处理,引擎控制整个的处理逻辑
* @return the <code>Engine</code> that handles requests for all
* <code>Connectors</code> associated with this Service.
*/
public Engine getContainer();
public void setContainer(Engine engine);
public String getName();
public void setName(String name);
public Server getServer();
public void setServer(Server server);
public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);
public String getDomain();
public void addConnector(Connector connector);
public Connector[] findConnectors();
public void removeConnector(Connector connector);
public void addExecutor(Executor ex);
public Executor[] findExecutors();
public Executor getExecutor(String name);
public void removeExecutor(Executor ex);
Mapper getMapper();
}
Service
接口里可以是在很多个Connector
,其port
属性可以指定我们要监听的端口,每一个Connector
可以监听一个不同的端口。

Catalina
里的Service
是处理请求的服务,真正会交给引擎进行处理,引擎控制整个的处理逻辑。

Connector
类
4. 每一个Connector
可以监听一个不同的端口
public class Connector extends LifecycleMBeanBase {
private static final Log log = LogFactory.getLog(Connector.class);
...
public Connector() {
this("HTTP/1.1");
}
public Connector(String protocol) {
boolean apr = AprStatus.isAprAvailable() &&
AprStatus.getUseAprConnector();
ProtocolHandler p = null;
try {
p = ProtocolHandler.create(protocol, apr);
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
if (p != null) {
protocolHandler = p;
protocolHandlerClassName = protocolHandler.getClass().getName();
} else {
protocolHandler = null;
protocolHandlerClassName = protocol;
}
// Default for Connector depends on this system property
setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}
public Connector(ProtocolHandler protocolHandler) {
protocolHandlerClassName = protocolHandler.getClass().getName();
this.protocolHandler = protocolHandler;
// Default for Connector depends on this system property
setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}
...
/**
* Return a property from the protocol handler.
*
* @param name the property name
* @return the property value
*
* @deprecated Use {@link #getProperty(String)}. This will be removed in
* Tomcat 10 onwards.
*/
@Deprecated
public Object getAttribute(String name) {
return getProperty(name);
}
/**
* Set a property on the protocol handler.
*
* @param name the property name
* @param value the property value
*
* @deprecated Use {@link #setProperty(String, String)}. This will be
* removed in Tomcat 10 onwards.
*/
@Deprecated
public void setAttribute(String name, Object value) {
setProperty(name, String.valueOf(value));
}
/**
* @return the <code>Service</code> with which we are associated (if any).
*/
public Service getService() {
return this.service;
}
/**
* Set the <code>Service</code> with which we are associated (if any).
*
* @param service The service that owns this Engine
*/
public void setService(Service service) {
this.service = service;
}
...
/**
* @return the port number on which this connector is configured to listen
* for requests. The special value of 0 means select a random free port
* when the socket is bound.
*/
public int getPort() {
// Try shortcut that should work for nearly all uses first as it does
// not use reflection and is therefore faster.
if (protocolHandler instanceof AbstractProtocol<?>) {
return ((AbstractProtocol<?>) protocolHandler).getPort();
}
// Fall back for custom protocol handlers not based on AbstractProtocol
Object port = getProperty("port");
if (port instanceof Integer) {
return ((Integer) port).intValue();
}
// Usually means an invalid protocol has been configured
return -1;
}
/**
* Set the port number on which we listen for requests.
*
* @param port The new port number
*/
public void setPort(int port) {
setProperty("port", String.valueOf(port));
}
...
public String getProxyName() {
return this.proxyName;
}
public void setProxyName(String proxyName) {
if(proxyName != null && proxyName.length() > 0) {
this.proxyName = proxyName;
} else {
this.proxyName = null;
}
setProperty("proxyName", this.proxyName);
}
public int getProxyPort() {
return this.proxyPort;
}
public void setProxyPort(int proxyPort) {
this.proxyPort = proxyPort;
setProperty("proxyPort", String.valueOf(proxyPort));
}
public int getRedirectPort() {
return this.redirectPort;
}
public void setRedirectPort(int redirectPort) {
this.redirectPort = redirectPort;
setProperty("redirectPort", String.valueOf(redirectPort));
}
public int getRedirectPortWithOffset() {
return getRedirectPort() + getPortOffset();
}
public String getScheme() {
return this.scheme;
}
public void setScheme(String scheme) {
this.scheme = scheme;
}
...
public String getURIEncoding() {
return uriCharset.name();
}
public Charset getURICharset() {
return uriCharset;
}
public void setURIEncoding(String URIEncoding) {
try {
Charset charset = B2CConverter.getCharset(URIEncoding);
if (!CharsetUtil.isAsciiSuperset(charset)) {
log.error(sm.getString("coyoteConnector.notAsciiSuperset", URIEncoding));
}
uriCharset = charset;
} catch (UnsupportedEncodingException e) {
log.error(sm.getString("coyoteConnector.invalidEncoding", URIEncoding, uriCharset.name()), e);
}
}
public boolean getUseBodyEncodingForURI() {
return this.useBodyEncodingForURI;
}
public void setUseBodyEncodingForURI(boolean useBodyEncodingForURI) {
this.useBodyEncodingForURI = useBodyEncodingForURI;
setProperty("useBodyEncodingForURI", String.valueOf(useBodyEncodingForURI));
}
}

Engine
接口
5. Engine
引擎控制整个的处理逻辑
public interface Engine extends Container {
public String getDefaultHost();
public void setDefaultHost(String defaultHost);
public String getJvmRoute();
public void setJvmRoute(String jvmRouteId);
public Service getService();
public void setService(Service service);
}

Host
接口
6. Host
:主机。一个引擎可以有多个Host
,每一个Host代表一个虚拟域名映射系统,根据Host可以进行虚拟主机隔离。
该接口唯一实现类StandardHost
有一个addChild
方法可以添加一个子节点,首先会判断其传来的参数类型是不是Context
public class StandardHost extends ContainerBase implements Host {
private static final Log log = LogFactory.getLog(StandardHost.class);
...
/**
* Add a child Container, only if the proposed child is an implementation
* of Context.
*
* @param child Child container to be added
*/
@Override
public void addChild(Container child) {
if (!(child instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardHost.notContext"));
child.addLifecycleListener(new MemoryLeakTrackingListener());
// Avoid NPE for case where Context is defined in server.xml with only a
// docBase
Context context = (Context) child;
if (context.getPath() == null) {
ContextName cn = new ContextName(context.getDocBase(), true);
context.setPath(cn.getPath());
}
super.addChild(child);
}
...
}

Context
接口
7. 一个web应用就是一个context,一个web应用有很多个 servlet、Filter、listener
该接口其中一个实现类StandardContext
有一个addChild
方法可以添加一个子节点,首先会判断其传来的参数类型是不是Wrapper
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
private static final Log log = LogFactory.getLog(StandardContext.class);
...
@Override
public void addChild(Container child) {
// Global JspServlet
Wrapper oldJspServlet = null;
if (!(child instanceof Wrapper)) {
throw new IllegalArgumentException
(sm.getString("standardContext.notWrapper"));
}
boolean isJspServlet = "jsp".equals(child.getName());
// Allow webapp to override JspServlet inherited from global web.xml.
if (isJspServlet) {
oldJspServlet = (Wrapper) findChild("jsp");
if (oldJspServlet != null) {
removeChild(oldJspServlet);
}
}
super.addChild(child);
if (isJspServlet && oldJspServlet != null) {
/*
* The webapp-specific JspServlet inherits all the mappings
* specified in the global web.xml, and may add additional ones.
*/
String[] jspMappings = oldJspServlet.findMappings();
for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
addServletMappingDecoded(jspMappings[i], child.getName());
}
}
}
...
}

Wrapper
接口
8. 该接口其中一个实现类StandardWrapper
有一个addChild
方法,里面就什么都不能加了
@SuppressWarnings("deprecation") // SingleThreadModel
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
private final Log log = LogFactory.getLog(StandardWrapper.class); // must not be static
...
@Override
public void addChild(Container child) {
throw new IllegalStateException
(sm.getString("standardWrapper.notChild"));
}
...
}

每一个Servlet
被封装为一个Wrapper

三、tomcat核心组件生命周期
Lifecycle
生命周期
1. 关系:Server
-> Service
-> Connector
-> Engine
-> Host
-> Context
-> Wrapper
-> Servlet
(Servlet
接口没有继承Lifecycle
)

/**
* start()
* -----------------------------
* | |
* | init() |
* NEW -»-- INITIALIZING |
* | | | | ------------------«-----------------------
* | | |auto | | |
* | | \|/ start() \|/ \|/ auto auto stop() |
* | | INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»--- |
* | | | | |
* | |destroy()| | |
* | --»-----«-- ------------------------«-------------------------------- ^
* | | | |
* | | \|/ auto auto start() |
* | | STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»-----
* | \|/ ^ | ^
* | | stop() | | |
* | | -------------------------- | |
* | | | | |
* | | | destroy() destroy() | |
* | | FAILED ----»------ DESTROYING ---«----------------- |
* | | ^ | |
* | | destroy() | |auto |
* | --------»----------------- \|/ |
* | DESTROYED |
* | |
* | stop() |
* ----»-----------------------------»------------------------------
*/
public interface Lifecycle {
...
/**
* Add a LifecycleEvent listener to this component.
*
* @param listener The listener to add
*/
public void addLifecycleListener(LifecycleListener listener);
/**
* Get the life cycle listeners associated with this life cycle.
*
* @return An array containing the life cycle listeners associated with this
* life cycle. If this component has no listeners registered, a
* zero-length array is returned.
*/
public LifecycleListener[] findLifecycleListeners();
/**
* Remove a LifecycleEvent listener from this component.
*
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener);
/**
* Prepare the component for starting. This method should perform any
* initialization required post object creation. The following
* {@link LifecycleEvent}s will be fired in the following order:
* <ol>
* <li>INIT_EVENT: On the successful completion of component
* initialization.</li>
* </ol>
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void init() throws LifecycleException;
/**
* Prepare for the beginning of active use of the public methods other than
* property getters/setters and life cycle methods of this component. This
* method should be called before any of the public methods other than
* property getters/setters and life cycle methods of this component are
* utilized. The following {@link LifecycleEvent}s will be fired in the
* following order:
* <ol>
* <li>BEFORE_START_EVENT: At the beginning of the method. It is as this
* point the state transitions to
* {@link LifecycleState#STARTING_PREP}.</li>
* <li>START_EVENT: During the method once it is safe to call start() for
* any child components. It is at this point that the
* state transitions to {@link LifecycleState#STARTING}
* and that the public methods other than property
* getters/setters and life cycle methods may be
* used.</li>
* <li>AFTER_START_EVENT: At the end of the method, immediately before it
* returns. It is at this point that the state
* transitions to {@link LifecycleState#STARTED}.
* </li>
* </ol>
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() throws LifecycleException;
/**
* Gracefully terminate the active use of the public methods other than
* property getters/setters and life cycle methods of this component. Once
* the STOP_EVENT is fired, the public methods other than property
* getters/setters and life cycle methods should not be used. The following
* {@link LifecycleEvent}s will be fired in the following order:
* <ol>
* <li>BEFORE_STOP_EVENT: At the beginning of the method. It is at this
* point that the state transitions to
* {@link LifecycleState#STOPPING_PREP}.</li>
* <li>STOP_EVENT: During the method once it is safe to call stop() for
* any child components. It is at this point that the
* state transitions to {@link LifecycleState#STOPPING}
* and that the public methods other than property
* getters/setters and life cycle methods may no longer be
* used.</li>
* <li>AFTER_STOP_EVENT: At the end of the method, immediately before it
* returns. It is at this point that the state
* transitions to {@link LifecycleState#STOPPED}.
* </li>
* </ol>
*
* Note that if transitioning from {@link LifecycleState#FAILED} then the
* three events above will be fired but the component will transition
* directly from {@link LifecycleState#FAILED} to
* {@link LifecycleState#STOPPING}, bypassing
* {@link LifecycleState#STOPPING_PREP}
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() throws LifecycleException;
/**
* Prepare to discard the object. The following {@link LifecycleEvent}s will
* be fired in the following order:
* <ol>
* <li>DESTROY_EVENT: On the successful completion of component
* destruction.</li>
* </ol>
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void destroy() throws LifecycleException;
/**
* Obtain the current state of the source component.
*
* @return The current state of the source component.
*/
public LifecycleState getState();
/**
* Obtain a textual representation of the current component state. Useful
* for JMX. The format of this string may vary between point releases and
* should not be relied upon to determine component state. To determine
* component state, use {@link #getState()}.
*
* @return The name of the current component state.
*/
public String getStateName();
/**
* Marker interface used to indicate that the instance should only be used
* once. Calling {@link #stop()} on an instance that supports this interface
* will automatically call {@link #destroy()} after {@link #stop()}
* completes.
*/
public interface SingleUse {
}
}
Listener
1. 各个生命周期都可以添加、获取、移除一些监听器
public void addLifecycleListener(LifecycleListener listener);
/**
* Get the life cycle listeners associated with this life cycle.
*
* @return An array containing the life cycle listeners associated with this
* life cycle. If this component has no listeners registered, a
* zero-length array is returned.
*/
public LifecycleListener[] findLifecycleListeners();
/**
* Remove a LifecycleEvent listener from this component.
*
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener);

2. 生命周期
init
1. init
方法会对组件进行初始化
/**
* Prepare the component for starting. This method should perform any
* initialization required post object creation. The following
* {@link LifecycleEvent}s will be fired in the following order:
* <ol>
* <li>INIT_EVENT: On the successful completion of component
* initialization.</li>
* </ol>
* // TODO 组件初始化
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void init() throws LifecycleException;

LifecycleBase
是Lifecycle
接口的一个抽象实现类,其init
定义了组件初始化的步骤,其调用的initInternal()
由具体类实现,具体类实现其核心初始化步骤。
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}
/**
* Sub-classes implement this method to perform any instance initialisation
* required.
*
* @throws LifecycleException If the initialisation fails
*/
protected abstract void initInternal() throws LifecycleException;

start
2. start
方法会启动组件
/**
* Prepare for the beginning of active use of the public methods other than
* property getters/setters and life cycle methods of this component. This
* method should be called before any of the public methods other than
* property getters/setters and life cycle methods of this component are
* utilized. The following {@link LifecycleEvent}s will be fired in the
* following order:
* <ol>
* <li>BEFORE_START_EVENT: At the beginning of the method. It is as this
* point the state transitions to
* {@link LifecycleState#STARTING_PREP}.</li>
* <li>START_EVENT: During the method once it is safe to call start() for
* any child components. It is at this point that the
* state transitions to {@link LifecycleState#STARTING}
* and that the public methods other than property
* getters/setters and life cycle methods may be
* used.</li>
* <li>AFTER_START_EVENT: At the end of the method, immediately before it
* returns. It is at this point that the state
* transitions to {@link LifecycleState#STARTED}.
* </li>
* </ol>
* TODO 组件启动
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() throws LifecycleException;

LifecycleBase
是Lifecycle
接口的一个抽象实现类,其start
定义了组件启动的步骤
@Override
public final synchronized void start() throws LifecycleException {
// TODO 组件状态流转判断
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}

其调用的startInternal()
由具体类实现,具体类实现其核心启动步骤
/**
* Sub-classes must ensure that the state is changed to
* {@link LifecycleState#STARTING} during the execution of this method.
* Changing state will trigger the {@link Lifecycle#START_EVENT} event.
*
* If a component fails to start it may either throw a
* {@link LifecycleException} which will cause it's parent to fail to start
* or it can place itself in the error state in which case {@link #stop()}
* will be called on the failed component but the parent component will
* continue to start normally.
*
* @throws LifecycleException Start error occurred
*/
protected abstract void startInternal() throws LifecycleException;

stop
3. stop
方法会停止组件的运行
/**
* Gracefully terminate the active use of the public methods other than
* property getters/setters and life cycle methods of this component. Once
* the STOP_EVENT is fired, the public methods other than property
* getters/setters and life cycle methods should not be used. The following
* {@link LifecycleEvent}s will be fired in the following order:
* <ol>
* <li>BEFORE_STOP_EVENT: At the beginning of the method. It is at this
* point that the state transitions to
* {@link LifecycleState#STOPPING_PREP}.</li>
* <li>STOP_EVENT: During the method once it is safe to call stop() for
* any child components. It is at this point that the
* state transitions to {@link LifecycleState#STOPPING}
* and that the public methods other than property
* getters/setters and life cycle methods may no longer be
* used.</li>
* <li>AFTER_STOP_EVENT: At the end of the method, immediately before it
* returns. It is at this point that the state
* transitions to {@link LifecycleState#STOPPED}.
* </li>
* </ol>
*
* Note that if transitioning from {@link LifecycleState#FAILED} then the
* three events above will be fired but the component will transition
* directly from {@link LifecycleState#FAILED} to
* {@link LifecycleState#STOPPING}, bypassing
* {@link LifecycleState#STOPPING_PREP}
* TODO 组件停止
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() throws LifecycleException;

LifecycleBase
是Lifecycle
接口的一个抽象实现类,其stop
定义了组件停止的步骤,其调用的stopInternal()
由具体类实现,具体类实现其核心停止步骤。
@Override
public final synchronized void stop() throws LifecycleException {
if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) ||
LifecycleState.STOPPED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStopped", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
state = LifecycleState.STOPPED;
return;
}
if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.BEFORE_STOP_EVENT);
}
try {
if (state.equals(LifecycleState.FAILED)) {
// Don't transition to STOPPING_PREP as that would briefly mark the
// component as available but do ensure the BEFORE_STOP_EVENT is
// fired
fireLifecycleEvent(BEFORE_STOP_EVENT, null);
} else {
setStateInternal(LifecycleState.STOPPING_PREP, null, false);
}
stopInternal();
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.AFTER_STOP_EVENT);
}
setStateInternal(LifecycleState.STOPPED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.stopFail", toString());
} finally {
if (this instanceof Lifecycle.SingleUse) {
// Complete stop process first
setStateInternal(LifecycleState.STOPPED, null, false);
destroy();
}
}
}
/**
* Sub-classes must ensure that the state is changed to
* {@link LifecycleState#STOPPING} during the execution of this method.
* Changing state will trigger the {@link Lifecycle#STOP_EVENT} event.
*
* @throws LifecycleException Stop error occurred
*/
protected abstract void stopInternal() throws LifecycleException;

destroy
4. destroy
方法会销毁组件
/**
* Prepare to discard the object. The following {@link LifecycleEvent}s will
* be fired in the following order:
* <ol>
* <li>DESTROY_EVENT: On the successful completion of component
* destruction.</li>
* </ol>
* TODO 组件销毁
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void destroy() throws LifecycleException;

LifecycleBase
是Lifecycle
接口的一个抽象实现类,其destroy
定义了组件销毁的步骤,其调用的destroyInternal()
由具体类实现,具体类实现其核心销毁步骤。
@Override
public final synchronized void destroy() throws LifecycleException {
if (LifecycleState.FAILED.equals(state)) {
try {
// Triggers clean-up
stop();
} catch (LifecycleException e) {
// Just log. Still want to destroy.
log.error(sm.getString("lifecycleBase.destroyStopFail", toString()), e);
}
}
if (LifecycleState.DESTROYING.equals(state) || LifecycleState.DESTROYED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyDestroyed", toString()), e);
} else if (log.isInfoEnabled() && !(this instanceof Lifecycle.SingleUse)) {
// Rather than have every component that might need to call
// destroy() check for SingleUse, don't log an info message if
// multiple calls are made to destroy()
log.info(sm.getString("lifecycleBase.alreadyDestroyed", toString()));
}
return;
}
if (!state.equals(LifecycleState.STOPPED) && !state.equals(LifecycleState.FAILED) &&
!state.equals(LifecycleState.NEW) && !state.equals(LifecycleState.INITIALIZED)) {
invalidTransition(Lifecycle.BEFORE_DESTROY_EVENT);
}
try {
setStateInternal(LifecycleState.DESTROYING, null, false);
destroyInternal();
setStateInternal(LifecycleState.DESTROYED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.destroyFail", toString());
}
}
/**
* Sub-classes implement this method to perform any instance destruction
* required.
*
* @throws LifecycleException If the destruction fails
*/
protected abstract void destroyInternal() throws LifecycleException;

3. 获取组件状态
/**
* Obtain the current state of the source component.
*
* @return The current state of the source component.
*/
public LifecycleState getState();
/**
* Obtain a textual representation of the current component state. Useful
* for JMX. The format of this string may vary between point releases and
* should not be relied upon to determine component state. To determine
* component state, use {@link #getState()}.
*
* @return The name of the current component state.
*/
public String getStateName();

Container
容器
2. Container
的继承关系

addChild
1. 容器可以添加子容器
/**
* Add a new child Container to those associated with this Container,
* if supported. Prior to adding this Container to the set of children,
* the child's <code>setParent()</code> method must be called, with this
* Container as an argument. This method may thrown an
* <code>IllegalArgumentException</code> if this Container chooses not
* to be attached to the specified Container, in which case it is not added
*
* @param child New child Container to be added
*
* @exception IllegalArgumentException if this exception is thrown by
* the <code>setParent()</code> method of the child Container
* @exception IllegalArgumentException if the new child does not have
* a name unique from that of existing children of this Container
* @exception IllegalStateException if this Container does not support
* child Containers
*/
public void addChild(Container child);

getParent
2. 容器有父容器
/**
* Get the parent container.
* TODO 容器有父容器
* @return Return the Container for which this Container is a child, if
* there is one. If there is no defined parent, return
* <code>null</code>.
*/
public Container getParent();

Pipeline
3. 容器中还有管道
/**
* Return the Pipeline object that manages the Valves associated with
* this Container.
*
* @return The Pipeline
*/
public Pipeline getPipeline();

Valve
4. 阀门
每一个容器里面都有管道,管道里面有很多的Valve (阀门),阀门可以预处理请求
public interface Pipeline extends Contained {
/**
* @return the Valve instance that has been distinguished as the basic
* Valve for this Pipeline (if any).
*/
public Valve getBasic();
/**
* <p>Set the Valve instance that has been distinguished as the basic
* Valve for this Pipeline (if any). Prior to setting the basic Valve,
* the Valve's <code>setContainer()</code> will be called, if it
* implements <code>Contained</code>, with the owning Container as an
* argument. The method may throw an <code>IllegalArgumentException</code>
* if this Valve chooses not to be associated with this Container, or
* <code>IllegalStateException</code> if it is already associated with
* a different Container.</p>
*
* @param valve Valve to be distinguished as the basic Valve
*/
public void setBasic(Valve valve);
/**
* <p>Add a new Valve to the end of the pipeline associated with this
* Container. Prior to adding the Valve, the Valve's
* <code>setContainer()</code> method will be called, if it implements
* <code>Contained</code>, with the owning Container as an argument.
* The method may throw an
* <code>IllegalArgumentException</code> if this Valve chooses not to
* be associated with this Container, or <code>IllegalStateException</code>
* if it is already associated with a different Container.</p>
*
* <p>Implementation note: Implementations are expected to trigger the
* {@link Container#ADD_VALVE_EVENT} for the associated container if this
* call is successful.</p>
*
* @param valve Valve to be added
*
* @exception IllegalArgumentException if this Container refused to
* accept the specified Valve
* @exception IllegalArgumentException if the specified Valve refuses to be
* associated with this Container
* @exception IllegalStateException if the specified Valve is already
* associated with a different Container
*/
public void addValve(Valve valve);
/**
* @return the set of Valves in the pipeline associated with this
* Container, including the basic Valve (if any). If there are no
* such Valves, a zero-length array is returned.
*/
public Valve[] getValves();
/**
* Remove the specified Valve from the pipeline associated with this
* Container, if it is found; otherwise, do nothing. If the Valve is
* found and removed, the Valve's <code>setContainer(null)</code> method
* will be called if it implements <code>Contained</code>.
*
* <p>Implementation note: Implementations are expected to trigger the
* {@link Container#REMOVE_VALVE_EVENT} for the associated container if this
* call is successful.</p>
*
* @param valve Valve to be removed
*/
public void removeValve(Valve valve);
/**
* @return the Valve instance that has been distinguished as the basic
* Valve for this Pipeline (if any).
*/
public Valve getFirst();
/**
* Returns true if all the valves in this pipeline support async, false otherwise
* @return true if all the valves in this pipeline support async, false otherwise
*/
public boolean isAsyncSupported();
/**
* Identifies the Valves, if any, in this Pipeline that do not support
* async.
*
* @param result The Set to which the fully qualified class names of each
* Valve in this Pipeline that does not support async will be
* added
*/
public void findNonAsyncValves(Set<String> result);
}

阀门的方法
阀门是一个责任链,可以执行该阀门,也可以获取、修改下一个阀门
public interface Valve {
/**
* @return the next Valve in the pipeline containing this Valve, if any.
*/
public Valve getNext();
/**
* Set the next Valve in the pipeline containing this Valve.
*
* @param valve The new next valve, or <code>null</code> if none
*/
public void setNext(Valve valve);
public void backgroundProcess();
/**
* ...
*
* @param request The servlet request to be processed
* @param response The servlet response to be created
*
* @exception IOException if an input/output error occurs, or is thrown
* by a subsequently invoked Valve, Filter, or Servlet
* @exception ServletException if a servlet error occurs, or is thrown
* by a subsequently invoked Valve, Filter, or Servlet
*/
public void invoke(Request request, Response response)
throws IOException, ServletException;
public boolean isAsyncSupported();
}

访问日志阀门
在server.xml
里配置了一个AccessLogValve
,访问日志阀门
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />

AccessLogValve
类可以记录日志
/**
* Log the specified message to the log file, switching files if the date
* has changed since the previous log call.
*
* @param message Message to be logged
*/
@Override
public void log(CharArrayWriter message) {
rotate();
/* In case something external rotated the file instead */
if (checkExists) {
synchronized (this) {
if (currentLogFile != null && !currentLogFile.exists()) {
try {
close(false);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
log.info(sm.getString("accessLogValve.closeFail"), e);
}
/* Make sure date is correct */
dateStamp = fileDateFormatter.format(
new Date(System.currentTimeMillis()));
open();
}
}
}
// Log this message
try {
message.write(System.lineSeparator());
synchronized(this) {
if (writer != null) {
message.writeTo(writer);
if (!buffered) {
writer.flush();
}
}
}
} catch (IOException ioe) {
log.warn(sm.getString(
"accessLogValve.writeFail", message.toString()), ioe);
}
}

四、tomcat启动的初始化流程
Bootstrap
对象
1.创建首先加锁创建一个Bootstrap
对象,然后调用其init
方法
public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
...
}

bootstrap.init()
2. 调用1. 初始化三个类加载器
该方法首先会调用initClassLoaders()
,初始化类加载器
public void init() throws Exception {
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}

其会加载tomcat
其中三个类加载器:commonLoader
、catalinaLoader
、sharedLoader
commonLoader
2. 是位于Tomcat 应用服务器顶层的公用类加载器,默认是加载${catalina.base}/lib
下的jar
包
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if (commonLoader == null) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader = this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}

加载的详细过程:(下面的不重要)

CatalinaProperties
类的静态代码块会调用静态的loadProperties()
方法,该方法会读取catalina.properties
文件
public class CatalinaProperties {
private static final Log log = LogFactory.getLog(CatalinaProperties.class);
private static Properties properties = null;
static {
loadProperties();
}
/**
* @param name The property name
* @return specified property value
*/
public static String getProperty(String name) {
return properties.getProperty(name);
}
/**
* Load properties.
*/
private static void loadProperties() {
InputStream is = null;
String fileName = "catalina.properties";
try {
// configUrl为null
String configUrl = System.getProperty("catalina.config");
if (configUrl != null) {
if (configUrl.indexOf('/') == -1) {
// No '/'. Must be a file name rather than a URL
fileName = configUrl;
} else {
is = (new URL(configUrl)).openStream();
}
}
} catch (Throwable t) {
handleThrowable(t);
}
if (is == null) {
try {
// B:\apache-tomcat-9.0.44-src (项目的根目录)
File home = new File(Bootstrap.getCatalinaBase());
// B:\apache-tomcat-9.0.44-src\conf
File conf = new File(home, "conf");
// B:\apache-tomcat-9.0.44-src\conf\catalina.properties
File propsFile = new File(conf, fileName);
is = new FileInputStream(propsFile);
} catch (Throwable t) {
handleThrowable(t);
}
}
if (is == null) {
try {
is = CatalinaProperties.class.getResourceAsStream
("/org/apache/catalina/startup/catalina.properties");
} catch (Throwable t) {
handleThrowable(t);
}
}
if (is != null) {
try {
properties = new Properties();
//加载配置文件
properties.load(is);
} catch (Throwable t) {
handleThrowable(t);
log.warn(t);
} finally {
try {
// 关闭文件输入流
is.close();
} catch (IOException ioe) {
log.warn("Could not close catalina properties file", ioe);
}
}
}
if ((is == null)) {
// Do something
log.warn("Failed to load catalina properties file");
// That's fine - we have reasonable defaults.
properties = new Properties();
}
// Register the properties as system properties
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String value = properties.getProperty(name);
if (value != null) {
System.setProperty(name, value);
}
}
}
...
}

该文件会指定common.loader
加载的路径
#
#
## List of comma-separated paths defining the contents of the "common"
## classloader. Prefixes should be used to define what is the repository type.
## Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
## If left as blank,the JVM system loader will be used as Catalina's "common"
## loader.
## Examples:
## "foo": Add this folder as a class repository
## "foo/*.jar": Add all the JARs of the specified folder as class
## repositories
## "foo/bar.jar": Add bar.jar as a class repository
#
## Note: Values are enclosed in double quotes ("...") in case either the
## ${catalina.base} path or the ${catalina.home} path contains a comma.
## Because double quotes are used for quoting, the double quote character
## may not appear in a path.
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=

加载到lib
里的jar包后,调用replace(value)
方法,把${catalina.base}
替换为项目根路径
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
//"${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
//将【${catalina.base}】替换为项目根路径 "B:\apache-tomcat-9.0.44-src/lib","B:\apache-tomcat-9.0.44-src/lib/*.jar","B:\apache-tomcat-9.0.44-src/lib","B:\apache-tomcat-9.0.44-src/lib/*.jar"
value = replace(value);
List<Repository> repositories = new ArrayList<>();
String[] repositoryPaths = getPaths(value);
for (String repository : repositoryPaths) {
// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}
// Local repository
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositories.add(new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(new Repository(repository, RepositoryType.DIR));
}
}
return ClassLoaderFactory.createClassLoader(repositories, parent);
}

Bootstrap
类的静态代码块会调用System.getProperty("user.dir")
(返回B:\apache-tomcat-9.0.44-src
),然后根据路径初始化这些变量
protected String replace(String str) {
// Implementation is copied from ClassLoaderLogManager.replace(),
// but added special processing for catalina.home and catalina.base.
String result = str;
int pos_start = str.indexOf("${");
if (pos_start >= 0) {
StringBuilder builder = new StringBuilder();
int pos_end = -1;
while (pos_start >= 0) {
builder.append(str, pos_end + 1, pos_start);
pos_end = str.indexOf('}', pos_start + 2);
if (pos_end < 0) {
pos_end = pos_start - 1;
break;
}
String propName = str.substring(pos_start + 2, pos_end);
String replacement;
if (propName.length() == 0) {
replacement = null;
} else if (Constants.CATALINA_HOME_PROP.equals(propName)) {
replacement = getCatalinaHome();
} else if (Constants.CATALINA_BASE_PROP.equals(propName)) {
replacement = getCatalinaBase();
} else {
replacement = System.getProperty(propName);
}
if (replacement != null) {
builder.append(replacement);
} else {
builder.append(str, pos_start, pos_end + 1);
}
pos_start = str.indexOf("${", pos_end + 1);
}
builder.append(str, pos_end + 1, str.length());
result = builder.toString();
}
return result;
}

catalinaLoader
3. catalinaLoader
直接返回其父加载器commonLoader

private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
value = replace(value);
...
}

sharedLoader
4. sharedLoader
也是直接返回其父加载器commonLoader


Catalina
对象
2. 创建加载完类加载器后,将当前线程的类加载器设置为catalinaLoader
,然后使用catalinaLoader
类加载器创建Catalina
对象
public void init() throws Exception {
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues); //设置Catalina的父加载器为sharedLoader
// catalina的后台线程
catalinaDaemon = startupInstance;
}

获取到Catalina
对象的setParentClassLoader
方法,然携带sharedLoader
参数执行该方法,表示使用sharedLoader
作为parentClassLoader

3. 加载命令行参数
daemon.load(args)
方法
1. 调用public static void main(String args[]) {
synchronized (daemonLock) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
然后将command
初始化为start

然后加载命令行参数

catalinaDaemon.load()
2. 利用反射执行利用反射执行catalinaDaemon
的load()
方法
private void load(String[] arguments) throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled()) {
log.debug("Calling startup class " + method);
}
method.invoke(catalinaDaemon, param);
}

3. 解析 server.xml
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
// 初始化目录
initDirs();
// 初始化命名服务 (JNDI)
// Before digester - it may be needed
initNaming();
// 解析server.xml文件
// Parse main server.xml
parseServerXml(true);
Server s = getServer();
if (s == null) {
return;
}
...
}

首先获取B:\apache-tomcat-9.0.44-src\conf\server.xml
文件

准备解析xml文件
protected void parseServerXml(boolean start) {
// Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile();
...
ServerXml serverXml = null;
if (useGeneratedCode) {
serverXml = (ServerXml) Digester.loadGeneratedClass(xmlClassName);
}
if (serverXml != null) {
serverXml.load(this);
} else {
try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
// Create and execute our Digester
// Digester是解析xml的工具
Digester digester = start ? createStartDigester() : createStopDigester();
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
if (generateCode) {
digester.startGeneratingCode();
generateClassHeader(digester, start);
}
// 解析xml文件
digester.parse(inputSource);
if (generateCode) {
generateClassFooter(digester);
try (FileWriter writer = new FileWriter(new File(serverXmlLocation,
start ? "ServerXml.java" : "ServerXmlStop.java"))) {
writer.write(digester.getGeneratedCode().toString());
}
digester.endGeneratingCode();
Digester.addGeneratedClass(xmlClassName);
}
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
}
}
}

使用xml读取器读取server.xml
文件
/**
* Parse the content of the specified input source using this Digester.
* Returns the root element from the object stack (if any).
*
* @param input Input source containing the XML data to be parsed
* @return the root object
* @exception IOException if an input/output error occurs
* @exception SAXException if a parsing exception occurs
*/
public Object parse(InputSource input) throws IOException, SAXException {
configure();
// xml读取器读取xml文件
getXMLReader().parse(input);
return root;
}

调用super.parse(inputSource);
public void parse(InputSource inputSource)
throws SAXException, IOException {
if (fSAXParser != null && fSAXParser.fSchemaValidator != null) {
if (fSAXParser.fSchemaValidationManager != null) {
fSAXParser.fSchemaValidationManager.reset();
fSAXParser.fUnparsedEntityHandler.reset();
}
resetSchemaValidator();
}
super.parse(inputSource);
}

public void parse(InputSource inputSource)
throws SAXException, IOException {
// parse document
try {
XMLInputSource xmlInputSource =
new XMLInputSource(inputSource.getPublicId(),
inputSource.getSystemId(),
null);
xmlInputSource.setByteStream(inputSource.getByteStream());
xmlInputSource.setCharacterStream(inputSource.getCharacterStream());
xmlInputSource.setEncoding(inputSource.getEncoding());
parse(xmlInputSource);
}
...
}

inputSource
解析的所有资源都会放到fConfiguration
里
public void parse(XMLInputSource inputSource)
throws XNIException, IOException {
// null indicates that the parser is called directly, initialize them
if (securityManager == null) {
securityManager = new XMLSecurityManager(true);
fConfiguration.setProperty(Constants.SECURITY_MANAGER, securityManager);
}
if (securityPropertyManager == null) {
securityPropertyManager = new XMLSecurityPropertyManager();
fConfiguration.setProperty(Constants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager);
}
reset();
fConfiguration.parse(inputSource);
} // parse(XMLInputSource)

public void parse(XMLInputSource source) throws XNIException, IOException {
if (fParseInProgress) {
// REVISIT - need to add new error message
throw new XNIException("FWK005 parse may not be called while parsing.");
}
fParseInProgress = true;
try {
setInputSource(source);
parse(true);
} catch (XNIException ex) {
...
} finally {
fParseInProgress = false;
// close all streams opened by xerces
this.cleanup();
}
} // parse(InputSource)

public boolean parse(boolean complete) throws XNIException, IOException {
//
// reset and configure pipeline and set InputSource.
if (fInputSource != null) {
try {
fValidationManager.reset();
fVersionDetector.reset(this);
fConfigUpdated = true;
resetCommon();
short version = fVersionDetector.determineDocVersion(fInputSource);
if (version == Constants.XML_VERSION_1_1) {
initXML11Components();
configureXML11Pipeline();
resetXML11();
} else {
configurePipeline();
reset();
}
// mark configuration as fixed
fConfigUpdated = false;
// resets and sets the pipeline.
fVersionDetector.startDocumentParsing((XMLEntityHandler) fCurrentScanner, version);
fInputSource = null;
} catch (XNIException ex) {
...
}
}
try {
return fCurrentScanner.scanDocument(complete);
} catch (XNIException ex) {
...
}
} // parse(boolean):boolean

protected void configurePipeline() {
super.configurePipeline();
if (fXIncludeEnabled) {
// If the XInclude handler was not in the pipeline insert it.
if (fXIncludeHandler == null) {
fXIncludeHandler = new XIncludeHandler();
// add XInclude component
setProperty(XINCLUDE_HANDLER, fXIncludeHandler);
addCommonComponent(fXIncludeHandler);
fXIncludeHandler.reset(this);
}
...
}
else {
// Setup NamespaceContext
if (fCurrentNSContext != fNonXIncludeNSContext) {
fCurrentNSContext = fNonXIncludeNSContext;
setProperty(NAMESPACE_CONTEXT, fNonXIncludeNSContext);
}
}
} // configurePipeline()

configurePipeline
前面都是与DTD
有关的,addComponent((XMLComponent) fNonNSScanner)
会将解析到的资源添加到组件
protected void configurePipeline() {
...
// setup document pipeline
if (fFeatures.get(NAMESPACES) == Boolean.TRUE) {
if (fCurrentScanner != fNamespaceScanner) {
fCurrentScanner = fNamespaceScanner;
setProperty(DOCUMENT_SCANNER, fNamespaceScanner);
setProperty(DTD_VALIDATOR, fDTDValidator);
}
fNamespaceScanner.setDTDValidator(fDTDValidator);
fNamespaceScanner.setDocumentHandler(fDTDValidator);
fDTDValidator.setDocumentSource(fNamespaceScanner);
fDTDValidator.setDocumentHandler(fDocumentHandler);
if (fDocumentHandler != null) {
fDocumentHandler.setDocumentSource(fDTDValidator);
}
fLastComponent = fDTDValidator;
} else {
// create components
if (fNonNSScanner == null) {
fNonNSScanner = new XMLDocumentScannerImpl();
fNonNSDTDValidator = new XMLDTDValidator();
// add components
addComponent((XMLComponent) fNonNSScanner);
addComponent((XMLComponent) fNonNSDTDValidator);
}
...
}
...
} // configurePipeline()

xml解析到J哪些标签就创建出对象,然后都层级封装到Catalina里面

4. 解析标签
在StandardService
类里添加无参构造,给该无参构造打上断点
public StandardService(){
log.info("创建StandardService对象");
}

根据栈轨迹可以查到到AbstractSAXParser
类的startElement
方法。 fContentHandler.startElement(uri, localpart, element.rawname,fAttributesProxy);
可以解析到server.xml
文件里的标签
public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
throws XNIException {
try {
// SAX1
if (fDocumentHandler != null) {
// REVISIT: should we support schema-normalized-value for SAX1 events
//
fAttributesProxy.setAttributes(attributes);
fDocumentHandler.startElement(element.rawname, fAttributesProxy);
}
// SAX2
if (fContentHandler != null) {
...
fAugmentations = augs;
String uri = element.uri != null ? element.uri : "";
String localpart = fNamespaces ? element.localpart : "";
fAttributesProxy.setAttributes(attributes);
fContentHandler.startElement(uri, localpart, element.rawname,
fAttributesProxy);
}
}
catch (SAXException e) {
throw new XNIException(e);
}
} // startElement(QName,XMLAttributes)

@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes list)
throws SAXException {
...
// Fire "begin" events for all relevant rules
List<Rule> rules = getRules().match(namespaceURI, match);
matches.push(rules); //封装解析到的标签信息
if ((rules != null) && (rules.size() > 0)) {
for (Rule value : rules) {
try {
Rule rule = value;
if (debug) {
log.debug(" Fire begin() for " + rule);
}
rule.begin(namespaceURI, name, list); // 开始解析
} catch (Exception e) {
log.error(sm.getString("digester.error.begin"), e);
throw createSAXException(e);
} catch (Error e) {
log.error(sm.getString("digester.error.begin"), e);
throw e;
}
}
} else {
if (debug) {
log.debug(sm.getString("digester.noRulesFound", match));
}
}
}

解析标签的信息,然后创建对象
public class ObjectCreateRule extends Rule {
...
protected String attributeName = null;
protected String className = null;
...
@Override
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
String realClassName = getRealClassName(attributes);
if (realClassName == null) {
throw new NullPointerException(sm.getString("rule.noClassName", namespace, name));
}
// Instantiate the new object and push it on the context stack
Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.getConstructor().newInstance();
digester.push(instance);
StringBuilder code = digester.getGeneratedCode();
if (code != null) {
code.append(System.lineSeparator());
code.append(System.lineSeparator());
code.append(realClassName).append(' ').append(digester.toVariableName(instance)).append(" = new ");
code.append(realClassName).append("();").append(System.lineSeparator());
}
}
protected String getRealClassName(Attributes attributes) {
// Identify the name of the class to instantiate
String realClassName = className;
if (attributeName != null) {
String value = attributes.getValue(attributeName);
if (value != null) {
realClassName = value;
}
}
return realClassName;
}
...
}

如何查看className
在何处设的值?只需给protected String className = null;
打上断点,然后查看栈轨迹就行了

解析server.xml
值 (Host
里面并不会在此时扫描到所有应用Context的存在,加载Host
类后会扫描当前主机有多少个应用)

4. 服务器初始化
StandardServer
初始化
1. 解析为server.xml
文件后,调用getServer().init();
方法,对服务器进行初始化
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
// TODO 初始化目录
initDirs();
// TODO 初始化命名服务 (JNDI)
// Before digester - it may be needed
initNaming();
// TODO 解析server.xml文件
// Parse main server.xml
parseServerXml(true);
Server s = getServer();
if (s == null) {
return;
}
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// Start the new server TODO 服务执行初始化
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error(sm.getString("catalina.initError"), e);
}
}
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
}

getServer().init();
方法跳到了StandardServer
类的父类LifecycleBase
的init()
方法。然后其调用 initInternal();
,调用子类的该方法,执行初始化核心逻辑。
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}

@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize utility executor
reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
register(utilityExecutor, "type=UtilityExecutor");
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources JNDI初始化
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
...
}
// Initialize our defined Services
for (Service service : services) {
service.init(); // 初始化所有服务
}
}
initInternal()
会让globalNamingResources
(JNDI) 初始化

然后会让所有的所有的service
初始化

StandardService
初始化
2. service.init()
方法跳到了StandardService
类的父类LifecycleBase
的init()
方法。然后其调用 initInternal();
,调用子类的该方法,执行初始化核心逻辑。
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}

StandardService
先让引擎初始化
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}

StandardEngine
引擎初始化
1. @Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}

@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}

protected void initInternal() throws LifecycleException {
reconfigureStartStopExecutor(getStartStopThreads()); //配置线程池
super.initInternal();
}

注册一些可能用到的属性
@Override
protected void initInternal() throws LifecycleException {
// If oname is not null then registration has already happened via
// preRegister().
if (oname == null) {
mserver = Registry.getRegistry(null, null).getMBeanServer();
oname = register(this, getObjectNameKeyProperties());
}
}

MapperListener
监听器初始化
2. 监听器的初始化,这里面什么都初始化


@Override
protected void initInternal() throws LifecycleException {
// If oname is not null then registration has already happened via
// preRegister().
if (oname == null) {
mserver = Registry.getRegistry(null, null).getMBeanServer();
oname = register(this, getObjectNameKeyProperties());
}
}


Connector
连接器初始化
3. 
连接器
初始化会让协议处理器
初始化,为什么要让协议处理器
初始化呢?连接器监听8080端口,8080端口传过来的都是HTTP协议的数据
@Override // TODO 连接器要启动监听端口
protected void initInternal() throws LifecycleException {
super.initInternal();
if (protocolHandler == null) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
}
// Initialize adapter TODO 协议转换器
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
if (service != null) {
protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
}
// Make sure parseBodyMethodsSet has a default
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
getProtocolHandlerClassName()));
}
if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
getProtocolHandlerClassName()));
}
if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() &&
protocolHandler instanceof AbstractHttp11JsseProtocol) {
AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
(AbstractHttp11JsseProtocol<?>) protocolHandler;
if (jsseProtocolHandler.isSSLEnabled() &&
jsseProtocolHandler.getSslImplementationName() == null) {
// OpenSSL is compatible with the JSSE configuration, so use it if APR is available
jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
}
}
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}

AbstractHttp11Protocol
协议初始化
@Override
public void init() throws Exception {
// Upgrade protocols have to be configured first since the endpoint
// init (triggered via super.init() below) uses this list to configure
// the list of ALPN protocols to advertise
for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
configureUpgradeProtocol(upgradeProtocol);
}
super.init();
// Set the Http11Protocol (i.e. this) for any upgrade protocols once
// this has completed initialisation as the upgrade protocols may expect this
// to be initialised when the call is made
for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
upgradeProtocol.setHttp11Protocol(this);
}
}

@Override
public void init() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
logPortOffset();
}
if (oname == null) {
// Component not pre-registered so register it
oname = createObjectName();
if (oname != null) {
Registry.getRegistry(null, null).registerComponent(this, oname, null);
}
}
if (this.domain != null) {
ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
this.rgOname = rgOname;
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null);
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
// TODO 端口初始化
endpoint.init();
}

####### 端点(端口)初始化
public final void init() throws Exception {
if (bindOnInit) {
bindWithCleanup();
bindState = BindState.BOUND_ON_INIT;
}
if (this.domain != null) {
// Register endpoint (as ThreadPool - historical name)
oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
Registry.getRegistry(null, null).registerComponent(this, oname, null);
ObjectName socketPropertiesOname = new ObjectName(domain +
":type=SocketProperties,name=\"" + getName() + "\"");
socketProperties.setObjectName(socketPropertiesOname);
Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);
for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
registerJmx(sslHostConfig);
}
}
}

private void bindWithCleanup() throws Exception {
try {
bind();
} catch (Throwable t) {
// Ensure open sockets etc. are cleaned up if something goes
// wrong during bind
ExceptionUtils.handleThrowable(t);
unbind();
throw t;
}
}

####### ServerSocket
初始化
@Override
public void bind() throws Exception {
initServerSocket();
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open(getName());
}

在这里面开通道,绑端口,这就是java
里面nio
里最底层的东西。 (此时还没有接收数据)
@Override
public void bind() throws Exception {
initServerSocket(); // TODO 初始化ServerSocket
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
selectorPool.open(getName());
}
// Separated out to make it easier for folks that extend NioEndpoint to
// implement custom [server]sockets
protected void initServerSocket() throws Exception {
if (getUseInheritedChannel()) {
// Retrieve the channel provided by the OS
Channel ic = System.inheritedChannel();
if (ic instanceof ServerSocketChannel) {
serverSock = (ServerSocketChannel) ic;
}
if (serverSock == null) {
throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
}
} else if (getUnixDomainSocketPath() != null) {
SocketAddress sa = JreCompat.getInstance().getUnixDomainSocketAddress(getUnixDomainSocketPath());
serverSock = JreCompat.getInstance().openUnixDomainServerSocketChannel();
serverSock.bind(sa, getAcceptCount());
if (getUnixDomainSocketPathPermissions() != null) {
Path path = Paths.get(getUnixDomainSocketPath());
Set<PosixFilePermission> permissions =
PosixFilePermissions.fromString(getUnixDomainSocketPathPermissions());
if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
FileAttribute<Set<PosixFilePermission>> attrs = PosixFilePermissions.asFileAttribute(permissions);
Files.setAttribute(path, attrs.name(), attrs.value());
} else {
java.io.File file = path.toFile();
if (permissions.contains(PosixFilePermission.OTHERS_READ) && !file.setReadable(true, false)) {
log.warn(sm.getString("endpoint.nio.perms.readFail", file.getPath()));
}
if (permissions.contains(PosixFilePermission.OTHERS_WRITE) && !file.setWritable(true, false)) {
log.warn(sm.getString("endpoint.nio.perms.writeFail", file.getPath()));
}
}
}
} else {
serverSock = ServerSocketChannel.open(); // 开通道
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
serverSock.bind(addr, getAcceptCount()); // 绑定端口
}
serverSock.configureBlocking(true); //mimic APR behavior
}

@Override
public void bind() throws Exception {
initServerSocket(); // TODO 初始化ServerSocket
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed TODO 初始化SSL安全协议
initialiseSsl();
// TODO selector准备在池子里监听数据
selectorPool.open(getName());
}

然后端口初始化
结束,协议初始化
结束,连接器初始化
结束,service
初始化结束,server
初始化结束
五、服务器启动
StandardServer
启动
1. 主类调用daemon.start();
public static void main(String args[]) {
...
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
...
}
}

调用public void org.apache.catalina.startup.Catalina.start()
方法
public void start() throws Exception {
if (catalinaDaemon == null) {
init();
}
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
method.invoke(catalinaDaemon, (Object [])null);
}

然后调用StandardServer
的start()
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
...
if (await) {
await();
stop();
}
}

调用StandardServer
父类 (LifecycleBase
) 的start()
@Override
public final synchronized void start() throws LifecycleException {
// TODO 组件状态流转判断
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}

然后先初始化JDNI
,然后遍历services
执行 service.start();
方法 (下面的流程都和初始化类似)
@Override
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (Service service : services) {
service.start();
}
}
if (periodicEventDelay > 0) {
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
() -> startPeriodicLifecycleEvent(), 0, 60, TimeUnit.SECONDS);
}
}

StandardService
启动
2. 调用StandardService
的父类LifecycleMBeanBase
的start()
方法

调用StandardService
类的startInternal()
方法,实现核心逻辑
然后还是 启动引擎 -> 启动监听器 -> 启动连接器
@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}

StandardEngine
引擎启动
1. 
@Override
protected synchronized void startInternal() throws LifecycleException {
// Log our server identification information
if (log.isInfoEnabled()) {
log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
}
// Standard container startup
super.startInternal();
}

首先判断是不是集群模式,如果是集群模式则先启动集群。然后找到引擎下的所有子容器(Host、Context、Wrapper),然后交给线程池,并发启动。
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
Cluster cluster = getClusterInternal(); // TODO 如果是集群模式(我们的不是),获取集群
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start(); // TODO 启动集群
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
Container children[] = findChildren(); // TODO 找到引擎下的所有子容器(Host、Context、Wrapper)
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
results.add(startStopExecutor.submit(new StartChild(child)));
}
...
}

遍历子容器,将子容器封装为StartChild
(new StartChild(child))
),然后调用InlineExecutorService
类的submit(Callable<T> task)
方法

这个StartChild
是一个Callable
,里面的call()
就会执行 child.start()
方法
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}

调用InlineExecutorService
类父类AbstractExecutorService
的submit(Callable<T> task)
方法,执行这些线程并获得返回结果,并将获得的返回结果返回。【1、执行StartChild
类的call()
方法里的代码 2、调用child.start();
(LifecycleBase
类的start()
方法)3、调用具体类 (例如StandardHost
) 的startInternal()
方法 】
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}

StandardHost
异步启动
子容器子容器启动调用command.run();
方法,新开了一个线程
@Override
public void execute(Runnable command) {
synchronized (lock) {
if (shutdown) {
throw new RejectedExecutionException();
}
taskRunning = true;
}
command.run();
synchronized (lock) {
taskRunning = false;
if (shutdown) {
terminated = true;
lock.notifyAll();
}
}
}

由于是异步任务,直接点Step Into F7
进不来,可以在child.start();
上打个断点,然后再执行到下一个断点,此时可以看到已经新开了一个线程
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}

没有异步栈轨迹的,点击一个栈轨迹,右键选中Async Stack Traces

调用StandardHost
类的父类LifecycleBase
的start()
方法,然后调用StandardHost
类的startInternal()
方法,执行其核心逻辑

获得管道中的所有阀门,判断是否有与ErrorReportValve
相同名字的管道,然后调用super.startInternal();
方法
@Override
protected synchronized void startInternal() throws LifecycleException {
// Set error report valve
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves(); // 获得管道中的所有阀门
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve = ErrorReportValve.class.getName().equals(errorValve) ?
new ErrorReportValve() :
(Valve) Class.forName(errorValve).getConstructor().newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString(
"standardHost.invalidErrorReportValveClass",
errorValve), t);
}
}
super.startInternal();
}

子容器的子容器异步启动
该子容器没有子容器。根据栈轨迹可以直到,这是另一个线程,该子容器线程调用的super.startInternal();
方法,因此与父容器的线程一样,执行了同一个ContainerBase
类的startInternal()
方法(根据栈轨迹也可以看出父容器线程此时停在了ContainerBase
类的startInternal()
方法的results.add(startStopExecutor.submit(new StartChild(child)));
这一行,新建一个线程,执行子容器的start()
方法)

子容器的管道启动
子容器的子容器异步启动后,遍历子容器返回的结果,查看执行是否有异常。(我们的子容器没有子容器,因此results
为空,因此增强for里面的语句根本就不会执行)
然后再让管道启动((Lifecycle) pipeline).start();
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
Cluster cluster = getClusterInternal(); // TODO 如果是集群模式(我们的不是),获取集群
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start(); // TODO 启动集群
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
Container children[] = findChildren(); // TODO 找到引擎下的所有子容器(Host、Context、Wrapper)
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
results.add(startStopExecutor.submit(new StartChild(child)));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
try {
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}

调用StandardPipeline
类的父类LifecycleBase
的start()
方法,然后调用StandardPipeline
类的startInternal()
方法

将current
的阀门初始化为first
,如果当前为null
(first
为null
) 就把current
设置为基础的阀门。然后调用当前阀门的start()
方法,调用完成后当前阀门current
指向下一个阀门,然后再次调用current
的start()
方法,再将current
指向下一个阀门,直到没有下一个阀门为止
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
setState(LifecycleState.STARTING);
}


子容器的阀门启动
@Override
protected synchronized void startInternal() throws LifecycleException {
// Initialize the Date formatters
String format = getFileDateFormat();
fileDateFormatter = new SimpleDateFormat(format, Locale.US);
fileDateFormatter.setTimeZone(TimeZone.getDefault());
dateStamp = fileDateFormatter.format(new Date(System.currentTimeMillis()));
if (rotatable && renameOnRotate) {
restore();
}
open();
super.startInternal();
}

阀门的启动就是给阀门设置了一个状态
@Override
protected synchronized void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
}

StandardContext
子容器启动给StandardContext
类的startInternal()
方法的这样行打个断点,使其运行到这一行
WebappLoader webappLoader = new WebappLoader();

根据栈轨迹可以看出是StandardHost
子容器在启动阀门后的setState(LifecycleState.STARTING);
方法里
setState(LifecycleState.STARTING);

一步步调用HostConfig
类的deployDirectories(File appBase, String[] files)
方法里的results.add(es.submit(new DeployDirectory(this, cn, dir)));
启动的
results.add(es.submit(new DeployDirectory(this, cn, dir)));

StandardWrapper
子容器启动给StandardWrapper
类的startInternal()
方法的super.startInternal();
这一行打上断点

然后分析栈轨迹,可以发现,前面打的StandardContext
类的startInternal()
方法的WebappLoader webappLoader = new WebappLoader();
这一行的这个断点一直执行,即可执行到fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
这一行,然后在这里面启动的StandardWrapper
(没有新开线程)
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

下面是**子容器启动StandardContext
和子容器启动StandardWrapper
**栈轨迹对比图

管道启动
由于启动很多个线程都会调用ContainerBase
类的startInternal()
方法,不好回到主线程调用的这个方法,因此可以在MultiThrowable multiThrowable = null;
这里打一个条件为children.length>0
的断点(子容器的线程children.length
都为0
),让其回到主线程(注意查看栈轨迹,判断其是否只有一个Main
线程,当然自带的线程不算)

results
的长度为1
,获取到的异步执行的结果为null
,表示执行没有遇到异常

然后让管道启动(这里就和子容器里管道启动流程差不多)


此时的current
被赋为basic
了 (子容器的为AccessLogValve
)

MapperListener
监听器启动
2. 

添加监听器
@Override
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
Engine engine = service.getContainer();
if (engine == null) {
return;
}
findDefaultHost();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
registerHost(host);
}
}
}

子容器全部添加到监听器
private void addListeners(Container container) {
container.addContainerListener(this);
container.addLifecycleListener(this);
for (Container child : container.findChildren()) {
addListeners(child);
}
}

findChildren()
方法里只有一段加锁的代码
public Container[] findChildren() {
synchronized (children) {
Container results[] = new Container[children.size()];
return children.values().toArray(results);
}
}

然后再调用engine.findChildren()
方法 (这调用两遍同样参数的加锁方法不浪费性能吗?)
让后注册不是新建状态的Host
虚拟主机
@Override
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
Engine engine = service.getContainer();
if (engine == null) {
return;
}
findDefaultHost();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
registerHost(host);
}
}
}

Connector
连接器启动
3. 

协议处理器启动
@Override
protected void startInternal() throws LifecycleException {
// Validate settings before starting
String id = (protocolHandler != null) ? protocolHandler.getId() : null;
if (id == null && getPortWithOffset() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}

端点启动
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
logPortOffset();
}
endpoint.start();
monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
() -> {
if (!isPaused()) {
startAsyncTimeout();
}
}, 0, 60, TimeUnit.SECONDS);
}

public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bindWithCleanup();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}

处理请求核心
worker
集合
创建10个worker
就是真正干活的,这里创建了十个worker
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
if (socketProperties.getProcessorCache() != 0) {
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
}
if (socketProperties.getEventCache() != 0) {
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
}
if (socketProperties.getBufferPool() != 0) {
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
}
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
// Start poller thread
poller = new Poller();
Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
startAcceptorThread();
}
}

创建有十个线程的worker
线程池
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}

进行连接限制

创建限制锁 (这就是juc
里面CountDownLatch
里的内容)
protected LimitLatch initializeConnectionLatch() {
if (maxConnections==-1) return null;
if (connectionLimitLatch==null) {
connectionLimitLatch = new LimitLatch(getMaxConnections()); // 最大处理8*1024=8192
}
return connectionLimitLatch;
}

Poller
创建一个创建一个Poller
,然后单线程启动poller
(拉取者),8080
端口

@Override // 这里面都是nio的写法
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
hasEvents = events(); // TODO 监听事件
if (wakeupCounter.getAndSet(-1) > 0) {
// If we are here, means we have other stuff to do
// Do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
// Either we timed out or we woke up, process events first
if (keyCount == 0) {
hasEvents = (hasEvents | events());
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
continue;
}
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
iterator.remove();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper != null) {
processKey(sk, socketWrapper); // TODO 处理事件
}
}
// Process timeouts
timeout(keyCount,hasEvents);
}
getStopLatch().countDown();
}
Acceptor
创建一个然后调用startAcceptorThread();
启动一个接受者线程,在后台接收数据。

这个Acceptor
也是只创建一个,然后单线程启动,监听8080
端口
protected void startAcceptorThread() {
acceptor = new Acceptor<>(this);
String threadName = getName() + "-Acceptor";
acceptor.setThreadName(threadName);
Thread t = new Thread(acceptor, threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon()); // 设置为守护线程
t.start();
}

@Override
public void run() {
int errorDelay = 0;
try {
// Loop until we receive a shutdown command
while (!stopCalled) { // TODO 服务器不是停止状态
// Loop if endpoint is paused
while (endpoint.isPaused() && !stopCalled) { // TODO 端口停了并且服务器没关,就将AcceptorState的状态设置为PAUSED
state = AcceptorState.PAUSED;
try {
Thread.sleep(50); // TODO 每隔50ms循环检查
} catch (InterruptedException e) {
// Ignore
}
}
if (stopCalled) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
endpoint.countUpOrAwaitConnection();
// Endpoint might have been paused while waiting for latch
// If that is the case, don't accept new connections
if (endpoint.isPaused()) {
continue;
}
U socket = null;
try {
// Accept the next incoming connection from the server
// socket TODO 不断地调用 serverSock.accept() 接收数据 【java.nio.channels里的ServerSocketChannel通道的accept()】
socket = endpoint.serverSocketAccept();
} catch (Exception ioe) {
// We didn't get a socket
endpoint.countDownConnection();
if (endpoint.isRunning()) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
} else {
break;
}
}
// Successful accept, reset the error delay
...
} finally {
stopLatch.countDown();
}
state = AcceptorState.ENDED;
}
监听给服务器发送的命令
切换到Catalina
类的start()
方法的getServer().start();
这个栈,在后面的await();
方法上打个断点

调用getServer().await();
public void await() {
getServer().await();
}

然后在这里面服务器一直等待接收命令,这里才是java.net
里的ServerSocket
使用操作系统底层网卡的8005
端口接收数据 (这里接收的数据为二进制流)
@Override
public void await() {
...
// Set up a server socket to wait on
try { // java.net里的ServerSocket
awaitSocket = new ServerSocket(getPortWithOffset(), 1,
InetAddress.getByName(address));
} catch (IOException e) {
log.error(sm.getString("standardServer.awaitSocket.fail", address,
String.valueOf(getPortWithOffset()), String.valueOf(getPort()),
String.valueOf(getPortOffset())), e);
return;
}
try {
awaitThread = Thread.currentThread();
// Loop waiting for a connection and a valid command
while (!stopAwait) {
ServerSocket serverSocket = awaitSocket;
if (serverSocket == null) {
break;
}
// Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder();
try {
InputStream stream;
long acceptStartTime = System.currentTimeMillis();
try { // TODO 使用8005端口,用于对服务器执行命令
socket = serverSocket.accept(); // TODO 开启端口接收数据(这里开的是操作系统底层网卡的端口,这里接收的数据为二进制流)
socket.setSoTimeout(10 * 1000); // Ten secondserverSocket = {ServerSocket@2613} "ServerSocket[addr=localhost/127.0.0.1,localport=8005]"sserverSocket =serverSocket = {ServerSocket@2613} "ServerSocket[addr=localhost/127.0.0.1,localport=8005]" {ServerSocket@2613} "ServerSocket[addr=localhost/127.0.0.1,localport=8005]"
stream = socket.getInputStream();
} catch (...) {
......
}

此处服务器就启动完成了
Accepor
、Poller
、Worker
关系
六、
1、 Accepor
一直接受8080
的请求, 会尝试endpoint.setSocketOptions(socket)
2、socke
t封装为socketWrapper
3、poller
先来注册事件socketWrapper
4、创建一个PollerEvent
,添加到事件队列SynchronizedQueue < PollerEvent>
1、Poller
一 直判断是否有事件events.poll();
事件就拿到 2、读取socket
的内容并处理processSocket (socketWrapper)
3、processSocket
的时候poller
会拿到Worker
线程池,我们的socketWrapper
会被封装到SocketProcessorBase
里面,把这个SocketProcessorBase
直接给线程池 4、SocketProcessorBase
会被线程池的一个线程进行处理,最终会被ConnectorHandler.process
进行处理。交给Http11Processor.process
进行处理 5、Http11Processor.service
会接手这个Socket
Acceptor
接收数据
1. 给Acceptor<U>
类的run()
方法的socket = endpoint.serverSocketAccept();
和if (!endpoint.setSocketOptions(socket)) {
打上断点

重启tomcat
,浏览器访问localhost:8080
,此时会先调用endpoint.serverSocketAccept();
然后调用endpoint.setSocketOptions(socket)
方法

NioEndpoint
类的setSocketOptions(SocketChannel socket)
方法会调用poller.register(socketWrapper);
向poller
里注册一个NioEndpoint$NioSocketWrapper
(NioEndpoint
的内部类NioSocketWrapper
)

接收到的数据放到事件队列
public void register(final NioSocketWrapper socketWrapper) {
socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into. TODO 准备读数据
PollerEvent event = null;
if (eventCache != null) {
event = eventCache.pop();
}
if (event == null) {
event = new PollerEvent(socketWrapper, OP_REGISTER); // TODO 新建一个PollerEvent
} else {
event.reset(socketWrapper, OP_REGISTER);
}
addEvent(event); // TODO 添加到事件队列【SynchronizedQueue<PollerEvent>】
}

调用NioEndpoint
类的内部类Poller
的addEvent
方法
private void addEvent(PollerEvent event) {
events.offer(event);
if (wakeupCounter.incrementAndGet() == 0) {
selector.wakeup();
}
}

很明显这是向队列中添加一个元素
public synchronized boolean offer(T t) {
queue[insert++] = t;
// Wrap
if (insert == size) {
insert = 0;
}
if (insert == remove) {
expand();
}
return true;
}

Poller
获取事件队列
然后给Poller
类的内部类Poller
的events();
方法打个断点,该方法会循环监听事件
@Override
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
hasEvents = events(); // TODO 监听事件
if (wakeupCounter.getAndSet(-1) > 0) {
// If we are here, means we have other stuff to do
// Do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
...
}
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
iterator.remove();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper != null) {
processKey(sk, socketWrapper); // TODO 处理事件
}
}
// Process timeouts
timeout(keyCount,hasEvents);
}
getStopLatch().countDown();
}

然后events
方法会调用events.poll()
把事件拿出来
public boolean events() {
boolean result = false;
PollerEvent pe = null;
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
...
}
}

worker
封装数据,交给判断到有事件后,调用processKey(sk, socketWrapper);
方法读通道里的数据,处理事件

protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
try {
if (close) {
cancelledKey(sk, socketWrapper);
} else if (sk.isValid() && socketWrapper != null) {
if (sk.isReadable() || sk.isWritable()) {
if (socketWrapper.getSendfileData() != null) {
processSendfile(sk, socketWrapper, false);
} else {
unreg(sk, socketWrapper, sk.readyOps()); // TODO 防止重读读取socket的数据
boolean closeSocket = false;
// Read goes before write
if (sk.isReadable()) { // TODO 判断当前是否是读状态
if (socketWrapper.readOperation != null) {
if (!socketWrapper.readOperation.process()) {
closeSocket = true;
}
} else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) { // TODO 处理Socket
closeSocket = true;
}
}
......
}
}

在里面获取到worker
的线程池,然后直接让worker
线程池执行NioEndpoint$SocketProcessor
(SocketProcessor
继承了SocketProcessorBase<NioChannel>
,SocketProcessorBase<S>
实现了Runnable
)
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = null;
if (processorCache != null) {
sc = processorCache.pop();
}
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else { // sc重置
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}

worker
处理数据
给SocketProcessorBase<S>
类的run()
方法的第一行打上断点,然后执行下一个断点就来到了这
@Override
public final void run() {
synchronized (socketWrapper) {
// It is possible that processing may be triggered for read and
// write at the same time. The sync above makes sure that processing
// does not occur in parallel. The test below ensures that if the
// first event to be processed results in the socket being closed,
// the subsequent events are not processed.
if (socketWrapper.isClosed()) {
return;
}
doRun();
}
}

调用doRun();
方法

调用AbstractProtocol<S>
类的内部类ConnectionHandler<S>
的process(SocketWrapperBase<S> wrapper, SocketEvent status)
方法
@Override
protected void doRun() {
/*
* Do not cache and re-use the value of socketWrapper.getSocket() in
* this method. If the socket closes the value will be updated to
* CLOSED_NIO_CHANNEL and the previous value potentially re-used for
* a new connection. That can result in a stale cached value which
* in turn can result in unintentionally closing currently active
* connections.
*/
Poller poller = NioEndpoint.this.poller;
if (poller == null) {
socketWrapper.close();
return;
}
try {
int handshake = -1;
try { // HTTPS的握手环节(我们没有使用https)
if (socketWrapper.getSocket().isHandshakeComplete()) {
...
} else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
event == SocketEvent.ERROR) {
...
} else {
...
}
} catch (..) {
...
}
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event); //处理socket
}
if (state == SocketState.CLOSED) {
poller.cancelledKey(getSelectionKey(), socketWrapper);
}
} else if (...) {
...
}
} catch (...) {
...
} finally {
socketWrapper = null;
event = null;
//return to cache
if (running && !paused && processorCache != null) {
processorCache.push(this);
}
}
}

把Http11Processor
注册进去
@Override
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
...
// TODO 先拿到真正的socket
S socket = wrapper.getSocket();
...
ContainerThreadMarker.set();
try {
...
if (processor == null) {
processor = recycledProcessors.pop(); // 查看已回收的协议里面有没有
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.processorPop", processor));
}
}
if (processor == null) {
processor = getProtocol().createProcessor(); // TODO 拿到当前协议,创建一个processor
register(processor);
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
}
}
// 设置ssl支持
processor.setSslSupport(
wrapper.getSslSupport(getProtocol().getClientCertProvider()));
// Associate the processor with the connection
wrapper.setCurrentProcessor(processor);
SocketState state = SocketState.CLOSED;
do {
state = processor.process(wrapper, status); // TODO 处理器处理Socket数据
...
} while ( state == SocketState.UPGRADING);
......
}

然后处理器处理Socket数据

当前SocketEvent
是读状态
@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {
SocketState state = SocketState.CLOSED; // TODO 先把Socket的状态设置为关闭
Iterator<DispatchType> dispatches = null;
do { // TODO 判断SocketEvent的状态
if (dispatches != null) {
DispatchType nextDispatch = dispatches.next();
if (getLog().isDebugEnabled()) {
getLog().debug("Processing dispatch type: [" + nextDispatch + "]");
}
state = dispatch(nextDispatch.getSocketStatus());
if (!dispatches.hasNext()) {
state = checkForPipelinedData(state, socketWrapper);
}
} else if (status == SocketEvent.DISCONNECT) {
// Do nothing here, just wait for it to get recycled
} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
state = dispatch(status);
state = checkForPipelinedData(state, socketWrapper);
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
} else if (status == SocketEvent.OPEN_READ) {
state = service(socketWrapper); // 当前`SocketEvent`是读状态
} else if (status == SocketEvent.CONNECT_FAIL) {
logAccess(socketWrapper);
} else {
// Default to closing the socket if the SocketEvent passed in
// is not consistent with the current state of the Processor
state = SocketState.CLOSED;
}
...
return state;
}

@Override
public SocketState service(SocketWrapperBase<?> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O TODO 初始化inputBuffer和outputBuffer
setSocketWrapper(socketWrapper);
...
while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
sendfileState == SendfileState.DONE && !protocol.isPaused()) {
// Parsing the request header
try {
if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(), // TODO 解析请求首行(获取超时事件、获取协议里的存活时间)
protocol.getKeepAliveTimeout())) {
if (inputBuffer.getParsingRequestLinePhase() == -1) {
return SocketState.UPGRADING;
} else if (handleIncompleteRequestLineRead()) {
break;
}
}
prepareRequestProtocol(); // TODO 准备请求协议
...
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.header.parse"), e);
}
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
break;
} catch (Throwable t) {
...
}
// Has an upgrade been requested?
if (isConnectionToken(request.getMimeHeaders(), "upgrade")) {
// Check the protocol
String requestedProtocol = request.getHeader("Upgrade");
UpgradeProtocol upgradeProtocol = protocol.getUpgradeProtocol(requestedProtocol);
if (upgradeProtocol != null) {
if (upgradeProtocol.accept(request)) {
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS); // TODO 给response里设置一些东西
response.setHeader("Connection", "Upgrade");
response.setHeader("Upgrade", requestedProtocol);
...
return SocketState.UPGRADING;
}
}
}
if (getErrorState().isIoAllowed()) {
// Setting up filters, and parse some request headers
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
try {
prepareRequest(); // TODO 准备请求 (解析请求头的信息,例如user-agent、host等)
} catch (Throwable t) {
...
}
}
...
// Process the request in the adapter
if (getErrorState().isIoAllowed()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
getAdapter().service(request, response); // TODO 调用真正的请求处理流程
if(keepAlive && !getErrorState().isError() && !isAsync() &&
statusDropsConnection(response.getStatus())) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
} catch (...) {
...
}
}
......
}

prepareRequest()
方法会解析请求头里的各种数据

让Http11Processor
类的service(SocketWrapperBase<?> socketWrapper)
方法一直执行到这,这里处理request
和response
,执行真正的请求处理逻辑

CoyoteAdapter
类的service(org.apache.coyote.Request req, org.apache.coyote.Response res)
方法就是真正的请求处理了
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
// 把org.apache.coyote的Request强转为org.apache.catalina.connector的Request(connector的Request实现了javax.servlet.http的HttpServletRequest)
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES); // 强转Response
if (request == null) { // TODO req.getNote(ADAPTER_NOTES)里的请求为空
// Create objects TODO 准备请求和响应
request = connector.createRequest(); // 创建一个connector的请求
request.setCoyoteRequest(req); // 设置coyote的Request为req
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding TODO 设置查询字符串的编码格式
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
}
boolean async = false;
boolean postParseSuccess = false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try {
// Parse and set Catalina and configuration specific
// request parameters TODO 后置解析请求 (解析scheme、处理url编码、SessionCookiesId、SessionSslId,根据情况[重定向]或[跳转到405错误页]或[放行请求])
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported( // TODO 异步处理支持(我们没用到异步)
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container TODO 获取service里的Engine,拿到管道,获取第一个阀门,然后执行
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
...
}
}

七、请求处理流程
这些方法的执行顺序
CoyoteAdapter
-> StandardEngineValve
-> StandardWrapperValve
(AbstractAccessLogValve
) -> ErrorReportValve
-> StandardHostValve
-> AuthenticatorBase
-> NonLoginAuthenticator
(StandardContextValve
) -> StandardWrapperValve

CoyoteAdapter
的service()
获取 Engine
引擎的第一个Valve
阀门
getService()
获取Service
,getContainer()
获取Engine
,getPipeline()
获取管道,getFirst()
获取第一个Valve
阀门,执行第一个阀门
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
// 把org.apache.coyote的Request强转为org.apache.catalina.connector的Request(connector的Request实现了javax.servlet.http的HttpServletRequest)
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES); // 强转Response
if (request == null) { // TODO req.getNote(ADAPTER_NOTES)里的请求为空
// Create objects TODO 准备请求和响应
request = connector.createRequest(); // 创建一个connector的请求
request.setCoyoteRequest(req); // 设置coyote的Request为req
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding TODO 设置查询字符串的编码格式
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
}
boolean async = false;
boolean postParseSuccess = false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try {
// Parse and set Catalina and configuration specific
// request parameters TODO 后置解析请求 (解析scheme、处理url编码、SessionCookiesId、SessionSslId,根据情况[重定向]或[跳转到405错误页]或[放行请求])
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported( // TODO 异步处理支持(我们没用到异步)
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container TODO 获取service里的Engine,拿到管道,获取第一个阀门,然后执行
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
...
}
}

如果有第一个阀门就返回第一个阀门,没有第一个阀门就返回basic
@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}

StandardEngineValve
引擎里的管道的第一个阀门会获取Host
主机,然后执行主机里的管道的第一个阀门
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
// HTTP 0.9 or HTTP 1.0 request without a host when no default host
// is defined.
// Don't overwrite an existing error
if (!response.isError()) {
response.sendError(404);
}
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}

AccessLogValve
将请求放到CachedElement
里缓存着,然后执行下一个阀门 (AccessLogValve
访问日志阀门)
由于AccessLogValve
类没有重写父类AbstractAccessLogValve
的invoke
方法,所以就跳到了父类的该方法
@Override
public void invoke(Request request, Response response) throws IOException,
ServletException {
if (tlsAttributeRequired) {
// The log pattern uses TLS attributes. Ensure these are populated
// before the request is processed because with NIO2 it is possible
// for the connection to be closed (and the TLS info lost) before
// the access log requests the TLS info. Requesting it now causes it
// to be cached in the request.
request.getAttribute(Globals.CERTIFICATES_ATTR);
}
if (cachedElements != null) {
for (CachedElement element : cachedElements) {
element.cache(request);
}
}
getNext().invoke(request, response);
}

ErrorReportValve
ErrorReportValve
阀门先让下一个阀门执行,执行完后再判断有没有出错
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
// Perform the request
getNext().invoke(request, response);
if (response.isCommitted()) {
if (response.setErrorReported()) {
// Error wasn't previously reported but we can't write an error
// page because the response has already been committed.
// See if IO is allowed
AtomicBoolean ioAllowed = new AtomicBoolean(true);
response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, ioAllowed);
if (ioAllowed.get()) {
// I/O is currently still allowed. Flush any data that is
// still to be written to the client.
try {
response.flushBuffer();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
// Now close immediately to signal to the client that
// something went wrong
response.getCoyoteResponse().action(ActionCode.CLOSE_NOW,
request.getAttribute(RequestDispatcher.ERROR_EXCEPTION));
}
}
return;
}
Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
// If an async request is in progress and is not going to end once this
// container thread finishes, do not process any error page here.
if (request.isAsync() && !request.isAsyncCompleting()) {
return;
}
if (throwable != null && !response.isError()) {
// Make sure that the necessary methods have been called on the
// response. (It is possible a component may just have set the
// Throwable. Tomcat won't do that but other components might.)
// These are safe to call at this point as we know that the response
// has not been committed.
response.reset();
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
// One way or another, response.sendError() will have been called before
// execution reaches this point and suspended the response. Need to
// reverse that so this valve can write to the response.
response.setSuspended(false);
try {
report(request, response, throwable);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
}

StandardHostValve
标准主机阀门
Host
的StandardHostValve
阀门又会主动调用Context
管道里的第一个阀门
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
Context context = request.getContext();
if (context == null) {
// Don't overwrite an existing error
if (!response.isError()) {
response.sendError(404);
}
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
try {
context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
...
try {
if (!response.isErrorReportRequired()) {
context.getPipeline().getFirst().invoke(request, response); //调用context管道里的第一个阀门
}
} catch (Throwable t) {
...
}
}
...
} finally {
// Access a session (if present) to update last accessed time, based
// on a strict interpretation of the specification
if (ACCESS_SESSION) {
request.getSession(false);
}
context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
}
}

NonLoginAuthenticator
这个阀门主要是做身份相关的

StandardContextValve
判断如果请求是"/META-INF"或"/WEB-INF/"下的直接发送错误,因此这些路径下的资源是受保护的
Context
的StandardContextValve
阀门又会主动调用Wrapper
管道里的第一个阀门
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Disallow any direct access to resources under WEB-INF or META-INF
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // TODO 如果请求是"/META-INF"或"/WEB-INF/"下的直接发送错误,因此这些路径下的资源是受保护的
return;
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Acknowledge the request
try {
response.sendAcknowledgement(ContinueResponseTiming.IMMEDIATELY);
} catch (IOException ioe) {
container.getLogger().error(sm.getString(
"standardContextValve.acknowledgeException"), ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request, response);
}

####### StandardWrapperValve
在StandardWrapperValve
类的invoke(Request request, Response response)
方法里调用wrapper.allocate()
创建一个javax.servlet.Servlet
对象 (每个请求都会调用wrapper.allocate()
,但只会创建一个同类型的Servlet,即没有才会创建)

####### 调试遇到的问题:instance
不为null
@Override
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
}
// Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
instance = loadServlet(); // 加载Servlet
newInstance = true;
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
}
if (!instanceInitialized) {
initServlet(instance);
}
}
}
...
}
}
访问jsp
时instance
会变为JspServlet
(例如:http://localhost:8080/index.jsp )

访问其他的资源时instance
会变为DefaultServlet
(例如:http://localhost:8080/docs/api/index.html )

老师的instance
为null
而我的试了几遍都不为null

给protected volatile Servlet instance = null;
打上断点

重启项目,最开始instance
是空的,自动调用 loadServlet()
后,instance
就非空了
@Override
public synchronized void load() throws ServletException {
instance = loadServlet();
if (!instanceInitialized) {
initServlet(instance);
}
if (isJspServlet) {
StringBuilder oname = new StringBuilder(getDomain());
oname.append(":type=JspMonitor");
oname.append(getWebModuleKeyProperties());
oname.append(",name=");
oname.append(getName());
oname.append(getJ2EEKeyProperties());
try {
jspMonitorON = new ObjectName(oname.toString());
Registry.getRegistry(null, null).registerComponent(instance, jspMonitorON, null);
} catch (Exception ex) {
log.warn(sm.getString("standardWrapper.jspMonitorError", instance));
}
}
}

这个StandardWrapper
类的loadServlet()
会返回一个DefaultServlet
,就是它让instance
不为null
的
public synchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance;
...
Servlet servlet;
try {
...
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass); //TODO 利用反射,创建Servlet对象
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
...
}
...
initServlet(servlet); // TODO 初始化刚刚创建的Servlet
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
...
}
return servlet;
}

调试的过程中还发现了获取当前Host
的所有应用的方法

protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}

####### 解决方法
点击instance
弹出框的左下角的Set value
重新将其设为null

这个StandardWrapper
类的loadServlet()
方法就是前面说的让instance
不为null
的方法
public synchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance;
PrintStream out = System.out;
if (swallowOutput) {
SystemLogHandler.startCapture();
}
Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
}
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass); //TODO 利用反射,创建Servlet对象
} catch (ClassCastException e) {
...
}
...
initServlet(servlet); // TODO 初始化刚刚创建的Servlet
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
...
}
return servlet;
}
loadServlet()
方法会利用反射,创建一个Servlet
对象

然后loadServlet()
方法调用initServlet(servlet);

initServlet(servlet);
方法调用了 servlet.init(facade);
方法
private synchronized void initServlet(Servlet servlet)
throws ServletException {
if (instanceInitialized && !singleThreadModel) return;
// Call the initialization method of this servlet
try {
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
servlet.init(facade);
}
instanceInitialized = true;
} catch (...) {
...
}
}

这 servlet.init(facade);
方法就初始化了一些参数

在StandardWrapperValve
类的invoke(Request request, Response response)
方法调用的wrapper.allocate()
方法初始化一个Servlet
后,会初始化一个filterChain
(过滤器链)

然后调用filterChain.doFilter(request.getRequest(), response.getResponse());
方法

filterChain.doFilter(request.getRequest(), response.getResponse());
会调用internalDoFilter(request,response);
方法
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
...
} else {
internalDoFilter(request,response); // TODO 内部过滤
}
}

如果有的话,会执行javax.servlet
包下的Filter
类的doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
方法
public final class ApplicationFilterChain implements FilterChain {
...
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
...
} else {
internalDoFilter(request,response); // TODO 内部过滤
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
...
if( Globals.IS_SECURITY_ENABLED ) {
...
} else {
filter.doFilter(request, response, this); // TODO 执行 javax.servlet.Filter.doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
...
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
...
} else {
servlet.service(request, response); // 执行封装的servlet.service(req,resp)
}
} catch (...) {
...
} finally {
...
}
}
...
}

filterChain
执行完没有任何异常就会调用servlet.service(request, response);
方法

HttpServlet抽象类继承的GenericServlet抽象类的Servlet接口里的service(ServletRequest req, ServletResponse res)方法,HttpServlet抽象类的service方法调用doGet(req, resp)、doPost(req, resp)等方法
package org.apache.catalina.servlets;
...
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
...
public class DefaultServlet extends HttpServlet {
...
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (req.getDispatcherType() == DispatcherType.ERROR) {
doGet(req, resp);
} else {
super.service(req, resp);
}
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Serve the requested resource, including the data content
serveResource(request, response, true, fileEncoding);
}
@Override
protected void doHead(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
...
}
@Override
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setHeader("Allow", determineMethodsAllowed(req));
}
protected String determineMethodsAllowed(HttpServletRequest req) {
StringBuilder allow = new StringBuilder();
// Start with methods that are always allowed
allow.append("OPTIONS, GET, HEAD, POST");
// PUT and DELETE depend on readonly
if (!readOnly) {
allow.append(", PUT, DELETE");
}
// Trace - assume disabled unless we can prove otherwise
if (req instanceof RequestFacade &&
((RequestFacade) req).getAllowTrace()) {
allow.append(", TRACE");
}
return allow.toString();
}
...
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
doGet(request, response);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
...
}
...
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
...
}
...
}
