Tomcat启动过程源码分析四
前言
上一篇文章中我们讨论了Bootstrap
类中main
方法中涉及到的init
方法,今天这篇文章我们来查看下load
方法。
daemon.setAwait(true);
daemon.load(args);
setAwait方法
在load
方法执行前,执行了setAwait
方法,跟进去查看
public void setAwait(boolean await)
throws Exception {
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Boolean.TYPE;
Object paramValues[] = new Object[1];
paramValues[0] = Boolean.valueOf(await);
Method method =
catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
method.invoke(catalinaDaemon, paramValues);
}
代码很简单,和上一篇文章中的类似,使用了反射调用了catalinaDaemon
对象的setAwait
方法,传递了参数true
,而我们在上一篇文章中,init
方法的最后讲解中可以看到catalinaDaemon
对象是Catalina
类的一个实例,所以查看Catalina
对象的setAwait
方法:
public void setAwait(boolean b) {
await = b;
}
看来daemon.setAwait(true)这句代码很简单,就是使用了反射,设置了catalina实例的await
属性为true
load方法
/**
* 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);
}
这段代码有了前面的经验读起来很容易,Bootstrap
类load
方法是使用了反射调用了Catalina
类的load
方法,我们继续查看Catalina
类的load
方法
/**
* Start a new server instance.
*/
public void load() {
long t1 = System.nanoTime();
//1 初始化相关属性
initDirs();
//2 初始化相关属性
initNaming();
//3 创建专门用来解析server.xml的Digester类,同时也隶属于Jakarta Commons项目,专门用来解析xml工具包
Digester digester = createStartDigester();
......
......
//到这里为止都是在解析server.xml
getServer().setCatalina(this);
// Stream redirection
initStreams();
// Start the new server
try {
//4 初始化一个Server实例
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("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
上面代码省略了部分,用中文注释注解出了相关含义,我们这里重点看下getServer().init()
,getServer
返回的是一个Server
的实例,很明显这里返回的应该是一个StandardServer
实例,我们继续查看StandardServer
的init
方法。
然而我们跟到StandardServer
类中没有直接的init
方法,查看其实现的接口发现他实现了Server
接口,Server
继承了LifeCycle
接口,StandardServer
继承了LifecycleMBeanBase
,LifecycleMBeanBase
继承了LifecycleBase
,而LifecycleBase
也实现了Lifecycle
,所以调用的init
方法应该是LifecycleBase
的init
方法。
@Override
public final synchronized void init() throws LifecycleException {
//1 一些通用的代码
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
//一些通用的代码
setStateInternal(LifecycleState.INITIALIZING, null, false);
try {
initInternal();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
//3 通用代码
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
我们可以看到,在LifeCycleBase
中除了通用的代码还调用了initInternal
方法。
我们看下在LifecycleBase
类中initInternal
方法做了什么。
protected abstract void initInternal() throws LifecycleException;
在抽象类LifeCycleBase
中只定义了initInternal
方法,并没有去实现而在StandardServer
中实现了initInternal
,所以调用了StandardServer
对象的init
方法实际上就是在调用initInternal
方法,我们来看看StandardServer
的initInternal
方法做了什么。
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
//不关心的代码 开始
......
......
//不关心的代码 结束
//关注的代码开始
//初始化Service
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
前面的一篇文章我们说过Tomcat的架构是什么样子的,Server
中包含多个Service
在initInternal
方法大末尾,我们看到了StandardServer
获取到了他内部所有Service
然后调用每个Service
的init
方法。那么这个service数组里面都包含了哪些service呢。其实在调用init
之前这个service数组就已经初始化好了,那么是在哪里初始化的呢?大家应该还记得上面的load
方法中有个类叫做Digester
,相关的代码Digester digester = createStartDigester();
,这个类在createStartDigester
方法中通过解析server.xml
文件,不仅来生成指定对象,更生成了不同对象之间的依赖关系,在这个方法内部,就将server
内部的service
全部都初始化了,其实StandardServer
根据server.xml
的格式默认只有一个service
,他的指定实现类就是StandardService
,关于digester
这个类,有机会可以单独写一篇文章讲解下使用方法。
我们现在知道了,在StandardServer
的init
方法中他调用了StandardService
的init
方法。我们继续查看StandardService
的init
方法。
和StandardServer
类似,调用init
方法实际上是调用了initInternal
方法。
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
//如果service内部的container为空那么就初始化
if (container != null) {
container.init();
}
// Initialize any Executors
//初始化executor,事实上在Service中代码走到这里的时候,findExecutors会返回空数组,这里的代码是不会执行的。
for (Executor executor : findExecutors()) {
if (executor instanceof LifecycleMBeanBase) {
((LifecycleMBeanBase) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize our defined Connectors
//初始化 connectors
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);
}
}
}
}
在StandardService
的init
方法中可以看出init
方法主要还是将Service
内部所有的connectors
全部轮流调用init
方法,是不是感觉很熟悉。是的!StandardService
内部所有的connectors
正是在server.xml
中定义的。那么默认的就是有两个了,分别对应处理http和ajp请求,我们加点代码打印下看看是不是这样。
测试代码
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
System.out.println("connector名称:"+connector.toString());
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);
}
}
}
最终输出如下
connector名称:Connector[HTTP/1.1-8080]
connector名称:Connector[AJP/1.3-8009]
好了,我们继续看connector
的init
方法,需要注意的是,类似server
,service
都是有指定标准实现类的,而connector
是没有standardconnector
这种实现类的,这主要是因为connector
根据不同的协议是有多个对应实现的,来一起看connector
的init
方法。
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
if( null == parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
// Initialize mapper listener
mapperListener.init();
}
你会发现,和StandardService
类似,Connector
也实现LifeCycle
接口,也实现了initInternal
方法。
现在大家肯定都有疑问,为什么都要实现
Lifecycle
这个接口,都要实现initInternal
方法,这个这里先说一下。Lifecycle
是管理所有tomcat组件的接口,只要这个组件实现了Lifecycle
这个接口,那么这个组件就可以随着tomcat启动而启动,随着tomcat停止而停止,一句话就是通过实现这个接口,tomcat可以统一管理所有组件的启动和停止,而组件启动的时候肯定会做一些通用的方法,所以有了init方法,而每个组件又会做一些自己的特有事情,所以init方法中又调用了initInternal
方法来让每个组件自己去实现自己特有的初始化事情,这种其实是一种设计模式,叫做模版设计模式,关于LifeCycle
和tomcat中常用的设计模式我们会单独写文章来说明,这里只是提一下,方便大家理解。
在connector
的initInternal
方法中,可以看到除了一些常规的方法以外,有两个方法需要关注一下,一个是protocolHandler.init()
,另外个就是mapperListener.init()
,而我们主要看一些关键组件,所以在这里就不讲解mapperListener.init()
,有兴趣的可以自行查看,提示:这个是一个监听器,监听的是容器内部的映射关系变化,我们主要看protocolHandler.init()
。
先看protocolHandler
这个东西是哪个类的实例,找到初始化的地方,查看Connector
的构造函数,可以看到:
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
实际上Connector是在解析server.xml
的时候实例化的,在<connector>
标签上可以指定很多属性,其中就包含该connector是哪个类的实例。
查看protocolHandlerClassName
可以看到
/**
* Coyote Protocol handler class name.
* Defaults to the Coyote HTTP/1.1 protocolHandler.
*/
protected String protocolHandlerClassName =
"org.apache.coyote.http11.Http11Protocol";
可以看出如果是处理http请求的Connector在init的时候,调用的是 org.apache.coyote.http11.Http11Protocol
这个类的init
方法,那我们继续查看类Http11Protocol
这个类的init
方法。
可以查看Http11Protocol
的继承类图:
Http11Protocol
本身没有init
方法,我们查看其父类,可以在类AbstractProtocol
中找到init
方法,如下:
@Override
public void init() throws Exception {
//其他代码
...
try {
endpoint.init();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.initError",
getName()), ex);
throw ex;
}
}
可以看到最后调用了endpoint.init()
,这个endPoint
指向的是哪个类呢?其实我们之前看到过,在Http11Protocol
的构造函数中有如下代码:
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
所以最后又调用了JIoEndpoint
的init
方法,我们先查看下JIoEndpoint
,可以发现其中并没有init
方法,查看其父类AbstractEndpoint
,发现有init
方法,方法如下:
public final void init() throws Exception {
testServerCipherSuitesOrderSupport();
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
其中bind
方法是个抽象方法,查看其子实现在类JIoEndpoint
中,
@Override
public void bind() throws Exception {
// Initialize thread count defaults for acceptor
//为acceptor 初始化线程数量
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1;
}
// Initialize maxConnections
//初始化最大连接数
if (getMaxConnections() == 0) {
// User hasn't set a value - use the default
setMaxConnections(getMaxThreadsExecutor(true));
}
//如果线程工厂类为空,初始化
if (serverSocketFactory == null) {
if (isSSLEnabled()) {
serverSocketFactory =
handler.getSslImplementation().getServerSocketFactory(this);
} else {
serverSocketFactory = new DefaultServerSocketFactory(this);
}
}
//初始化接收请求的线程。
if (serverSocket == null) {
try {
if (getAddress() == null) {
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog());
} else {
serverSocket = serverSocketFactory.createSocket(getPort(),
getBacklog(), getAddress());
}
} catch (BindException orig) {
String msg;
if (getAddress() == null)
msg = orig.getMessage() + " <null>:" + getPort();
else
msg = orig.getMessage() + " " +
getAddress().toString() + ":" + getPort();
BindException be = new BindException(msg);
be.initCause(orig);
throw be;
}
}
}
可以看出这个这个init方法就是初始化了几个比较重要的属性,包括线程数量,线程最大链接数,线程工厂类以及接收请求的线程。
到这里我们终于把connector
的init
方法查看完毕。
那么我们继续回到StandServer
内部的init
方法
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);
}
}
}
你会发现,connector
初始化完毕以后,StandardServer
的init
方法也调用结束了,也就是说load
方法到这里就结束了。