1. 源码入口
在bin目录下的catalina.bat中,有如下代码 set MAINCLASS=org.apache.catalina.startup.Bootstrap ,表明了tomcat的主类也就是启动类的位置。
启动类位置:org/apache/catalina/startup/Bootstrap.java
2. 初始化流程
2.1 主类初始化
2.1.1 调用init()方法进行初始化
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;
}
2.1.2 组类init()方法
init的作用主要是创建一个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");
// 通过反射创建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对象startupInstance赋值给catalinaDaemon
catalinaDaemon = startupInstance;
}
2.1.3 主类初始化之后执行load方法
主类初始化结束之后,就开始执行一些命令参数语句
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.");
}
}
通过命令参数,找到对应的load方法,这里以start为例:
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);
}
// 调用catalina的load方法
method.invoke(catalinaDaemon, param);
}
2.2 Catalina
接着执行catalina的load方法
public void load(String args[]) {
try {
if (arguments(args)) {
// 调用load的重载方法,执行逻辑
load();
}
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
然后有参的load调用无参的load方法
getServer().init();
2.2.1 创建tomcat的xml解析工具
// Digester是tomcat的xml文件解析根据,解析server.xml
Digester digester = createStartDigester();
2.2.2 得到Server对象
经过一系列的处理之后,在执行
getServer().init();
得到Server对象,然后调用Server对象的init()方法
2.3 Server
在tomcat的组件中,有一个生命周期的接口LifeCycle,其有一个实现类LifeCycleBase,getServer().init();调用的init其实就是LifeCycleBase中的init方法。
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());
}
}
发现,在init中,又调用了initInternal方法。initInternal是LifeCycleBase的抽象方法,所以最终调用的这个方法就是实现类中的initInternal方法,也即是调用的Server的实现类StandardServer的initInternal。
进入initInternal方法,这里又调用了service的init方法
// 一个Server下可以有多个service
for (int i = 0; i < services.length; i++) {
services[i].init();
}
2.4 Service
跟前面的过程一样,找到StandardService的initInternal,分别初始化引擎(engine)、执行器(Executor)、连接器(Connector)
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) {
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);
}
}
}
}
2.5 Connector
在Service中初始化了很多组件,这些组件初始化都是差不多的,这里着重介绍一下Connector。
Connector不是接口,所以直接调用其方法initInternal。
2.5.1 创建适配器
adapter = new CoyoteAdapter(this);
2.5.2 初始化protocolHandler
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
3. 启动流程
在BootStrap主函数中,load方法下面,接着就是start方法,接下来我们顺着代码看一下tomcat的启动流程。
daemon.start();
首先,调用自身的start方法,然后调用Catalina的start方法
public void start() throws Exception {
if (catalinaDaemon == null) {
init();
}
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
// 调用catalina的start方法
method.invoke(catalinaDaemon, (Object [])null);
}
然后,Catalina的start方法,在去调用Server的start方法
try {
getServer().start();
}
又采用了模板设计模式,走到了LifeCycleBase中,在start方法中通过startInternal方法执行各个接口的实现类的启动业务逻辑。
这里在重点说下Connector连接器对象的启动,在Connector中调用startInternal方法,然后调用protocolHandler的start方法
try {
protocolHandler.start();
}
最后程序会走到AbsTractEndpoint的start方法
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
执行startInternal()方法,因为是Http/1.1,所以会执行NioEndpoint的startInternal方法,在该方法的最后面,有一个开启线程的调用
// 开启线程
startAcceptorThreads();
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
// 接受客户端的请求对象
acceptors = new Acceptor[count];
// 开启线程
for (int i = 0; i < count; i++) {
// 创建线程对象
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
createAcceptor():
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
Acceptor():
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
state = AcceptorState.PAUSED;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
countUpOrAwaitConnection();
SocketChannel socket = null;
try {
// Accept the next incoming connection from the server
// socket
socket = serverSock.accept();
} catch (IOException ioe) {
// We didn't get a socket
countDownConnection();
if (running) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
} else {
break;
}
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused) {
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
if (!setSocketOptions(socket)) {
closeSocket(socket);
}
} else {
closeSocket(socket);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
state = AcceptorState.ENDED;
}
在Acceptor的run方法中,有一个一句话是用来接收客户端的请求的代码,如下:
socket = serverSock.accept();
接收并等待客户端的请求。
至此,启动流程结束!
总结:
从启动流程图与源码跟踪,可以看出tomcat的启动过程非常标准化,统一按照生命周期管理接口LifeCycle的定义进行启动的,首先调用init()方法进行组件的逐级初始化操作,然后再调用start()方法进行启动。
每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法,组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。