后台大部分功能是用java来实现的,因此想了解tomcat的底层实现。写下此文留做复习用吧。至于一些概念在一书《深入剖析tomcat》已经有很详细的分析了。 说明一下本文分析的tomcat版本为8.5.2。
开始:
首先找到org.apache.catalina.startup包下的Bootstrap类,它就是tomcat的启动类。找到main方法如下:
public static void main(String args[]) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
......
bootstrap.init();
......
daemon = bootstrap;
}
......
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
}
......
}
在这里先看 bootstrap.init()
public void init() throws Exception {
// 创建类的加载器
initClassLoaders();
......
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
......
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;
}
该方法通过反射的方法创建org.apache.catalina.startup.Catalina类的实例。
再回到main方法里面找到如下的代码,通过前面分析可知daemon就是在init方法里面创建的org.apache.catalina.startup.Catalina的实例。
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
}
找到Catalina类的load方法
public void load() {
long t1 = System.nanoTime();
......
Digester digester = createStartDigester(); // 设置了解析server.xml的规则
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
......
file = configFile(); // 这里打开的就是server.xml
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
......
// 解析server.xml文件并实例化对象
inputSource.setByteStream(inputStream); // 用作XML解析的用的
digester.push(this); // 压在栈顶,也就是为了设置server
digester.parse(inputSource);
......
getServer().init();
......
}
先看createStartDigester()看它是如何解析文件的。
protected Digester createStartDigester() {
......
Digester digester = new Digester();
......
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
......
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
......
digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
......
return (digester);
}
关于Digester的具体用法网上已经有很多了,在这里就不详细讲。只做一些简单的说明。
public void addObjectCreate(String pattern, String className,String attributeName)
第一个参数是匹配规则,如: <Server></Server>
第二个参数是创建默认对象的默认类
第三个参数是遇到匹配规则时用哪个属性来创建对象,如果没有则使用第二个参数传进去的默认类。
如createStartDigester里面的代码:
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
当遇到Server这个标签时,以className的属性来创建对象,如果该属性不存在,创建的对象则是org.apache.catalina.core.StandardServer。并把创建的对象放在Digester栈的栈顶。
public void addSetProperties(String pattern)
该方法是设置栈顶元素的属性 的。遇到指定的标签时,则把标签里面的属性设置到所处Digester栈,栈顶元素里面去。
public void addSetNext(String pattern, String methodName,
String paramType)
这个方法作用是当解析完该标签之后,则调用Digester栈里面比自己低一层的元素的methodName方法,将自己作为参数,并且把自己弹出栈。
回到org.apache.catalina.startup.Catalina的load方法。可以看到digester.push(this); Catalina类把自己压入digester栈中,看到createStartDigester解析server.xml的规则。
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
在这里通过查看server.xml知道Server标签的className是不存在的,因此创建的是StandardServer的实例对象,然后在然后通过addSetNext来调用Catalina方法的setServer来把自己设置到Catalina类里面。
最后在 Catalina的load方法里面调用getServer().init()。也就是StandardServer的init方法。该方法后面再详细分析。
Catalina的load方法处理完之后就是调用该类的start方法了,代码如下:
public void start() {
......
getServer().start();
......
}
可是该方法调用的是StandardServer的start方法。
从功能看到Catalina的load方法主要是解析server.xml文件并实例化里面的对象,而start方法主要是调用了StandardServer的start方法。
好了Catalina的启动过程就分析了,下面接着分析StandardServer类的方法。StandardServer的init方法是在父类LifecycleBase里面实现的,而该方法调用了StandardServer类里面的initInternal方法,带入如下:
protected void initInternal() throws LifecycleException {
......
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
在Catalina的createStartDigester方法
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
可以知道这里的services就是StandardService的实例对象。StandardService的init后面再分析。
接着分析StandardServer的start方法。该方法的实现也是在StandardServer的父类LifecycleBase里面实现的,该方法又调用了StandardServer的startInternal方法,代码如下:
protected void startInternal() throws LifecycleException {
......
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
由此可是该方法调用的是services的start方法。接着看到StandardService的init方法。该类的父类是LifecycleBase因此,直接看StandardService的initInternal方法即可,代码如下:
protected void initInternal() throws LifecycleException {
......
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
......
synchronized (connectorsLock) {
for (Connector connector : connectors) {
......
connector.init();
......
}
}
}
从代码可以看出该方法创建了初始化了线程池和Connector 。
接着看到StandardService的startInternal方法
protected void startInternal() throws LifecycleException {
......
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
......
synchronized (connectorsLock) {
for (Connector connector: connectors) {
......
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
......
}
}
}
该方法主要是启动线程池和连接器。