前言
上一篇文章中我们讨论了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方法到这里就结束了。