tomcat源码系列二(初始化启动)

Tomcat总体结构

Tomcat 的结构很复杂,但是 Tomcat 也非常的模块化,找到了 Tomcat 最核心的模块,您就抓住了 Tomcat 的“七寸”。下面是 Tomcat 的总体结构图:

结构图

从上图中可以看出 Tomcat 的心脏是两个组件:Connector 和 Container,关于这两个组件将在后面详细介绍。Connector 组件是可以被替换,这样可以提供给服务器设计者更多的选择,因为这个组件是如此重要,不仅跟服务器的设计的本身,而且和不同的应用场景也十分相关,所以一个 Container 可以选择对应多个 Connector。多个 Connector 和一个 Container 就形成了一个 Service,Service 的概念大家都很熟悉了,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。

通过关键类来看Tomcat结构

关键类

Tomcat启动

当通过 ./startup.sh 脚本或直接通过 java 命令来启动 Bootstrap 时,Tomcat 的启动过程就正式开始了,启动的入口点就是 Bootstrap 类的 main 方法。
启动的过程分为三步,分别为:

1
2
3
4
5
6
7
1. Bootstrap初始化
Bootstrap bootstrap = new Bootstrap();
bootstrap.init();
daemon = bootstrap;
2. load和start
daemon.load(args);
daemon.start();

bootstrap.init()

先贴出源码,在源码中标注了顺序,下面详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Initialize daemon.
*/
public void init()
throws Exception
{

//1. Set Catalina path
setCatalinaHome();
setCatalinaBase();

//2. 初始化classLoader
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.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);
//3. 设置parentClassLoader
method.invoke(startupInstance, paramValues);

// 4. 将catalinaDaemon赋值为Catalia实例
catalinaDaemon = startupInstance;

}
  1. Set Catalina path (设置catalina的路径信息)
  2. initClassLoaders(初始化三个ClassLoader)
  3. 设置Catalina的parentClassLoader
  4. 将catalinaDaemon赋值为Catalia实例

daemon.load(args)

贴出源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Load daemon.
*/
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);

}

可以看出这个做的主要事情是调用catalinaDaemon.load()

在第一步init的时候 知道catalinaDaemon为Catalina的实例,所以看下catalina.load(),其中的关键性代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1. Create and execute our Digester
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
// 2.读取配置文件conf/server.xml
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
// 3.解析配置文件
digester.parse(inputSource);
getServer().setCatalina(this);
// Stream redirection
initStreams();
// 4.初始化server
getServer().init();
  1. createStartDigester

    • 添加规定的对象创建规则

    • 为规定的对象添加设置属性规则

    • 设置父级方法 完成后调用

      举个例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      //添加规定的对象Server 对应Server.xml中的<Server>
      digester.addObjectCreate("Server",
      "org.apache.catalina.core.StandardServer",
      "className");
      digester.addSetProperties("Server");
      //设置上级调用setServer方法 此处看下面的digester.push(this),将catalina的实例放入了栈顶,即会调用Catalina.setServer()指定了catalina中的server为parse时创建的Server对象
      digester.addSetNext("Server",
      "setServer",
      "org.apache.catalina.Server");
    • 添加ruleSet

  2. 读取server.xml

  3. 重点步骤 digester.parse

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1. getXMLReader().parse(input);
    2. getXMLReader:getParser().getXMLReader();
    3. getParser():getFactory().newSAXParser();
    4. getFactory():SAXParserFactory.newInstance();
    5. newInstance():FactoryFinder.find(
    /* The default property name according to the JAXP spec */
    SAXParserFactory.class,
    /* The fallback implementation class name */
    "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
    可以得到第3步的getFactory()得到的实例为SAXParserFactoryImpl的实例
    6. 第3步的newSAXParser():new SAXParserImpl(this, features, fSecureProcess); 得到的是SAXParserImpl()的实例 所以getParser()为SAXParserImpl
    7. 最终调用的为:SAXParserImpl.parse(input)

    经过对xml文件的解析将会产生org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。

  4. 调用server.init()

    1
    2
    3
    setStateInternal(LifecycleState.INITIALIZING, null, false);
    initInternal();
    setStateInternal(LifecycleState.INITIALIZED, null, false);

    其中的重点方法为:initInternal(),initInternal()中的重点代码如下

    1
    2
    3
    4
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
    services[i].init();
    }

    同样的StandardService的init重点代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    //初始化container
    if (container != null) {
    container.init();
    }

    // 初始化Executor
    for (Executor executor : findExecutors()) {
    if (executor instanceof LifecycleMBeanBase) {
    ((LifecycleMBeanBase) executor).setDomain(getDomain());
    }
    executor.init();
    }

    // 初始化定义的Connector 在server.xml中的配置
    <Connector port="8080" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    synchronized (connectorsLock) {
    for (Connector connector : connectors) {
    try {
    connector.init();
    } catch (Exception e) {
    String message = sm.getString(
    "standardService.connector.initFailed", connector);
    log.error(message, e);

    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
    throw new LifecycleException(message);
    }
    }
    }

直到service的初始化 我们可以发现 service的初始化中包含了container.init()和多个Connector.init()对应了上文中的一个service包含了一个container和多个connector

container是从哪里实例化的呢,在进行createStartDigester的时候 会添加多个RuleSet如:digester.addRuleSet(new EngineRuleSet("Server/Service/"));
打断点可以发现如下在digester中的rules包含了如下几个规则:

1
2
ObjectCreateRule[className=org.apache.catalina.core.StandardEngine, attributeName=className]
SetNextRule[methodName=setContainer, paramType=org.apache.catalina.Container]

在进行digester.parse()的时候就会对这些规则进行解析,将StandardEngine的实例绑定为Service的Container

通过代码可以看到StandardServer、StandardService、StandardEnginee、Connector均继承了抽象类LifecycleMBeanBase,抽象类实现了LifecycleBase接口。

daemon.start()

lemon wechat
欢迎大家关注我的订阅号 SeeMoonUp
写的不错?鼓励一下?不差钱?