Tomcat源码系列文章
Tomcat源码解析(二): Bootstrap和Catalina
startup.bat
,它用于windows
环境下启动tomcatstartup.sh
,它用于linux
环境下启动tomcatBootstrap
这个类,catalina.bat最终执行了Bootstrap类中的main
方法来启动tomcat
set _EXECJAVA=%_RUNJAVA%
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
Bootstrap
作为启动入口首先进行了初始化方法init
然后load
方法加载了Catalina
Bootstrap
对象,调用它的init
方法初始化load
与start
方法private static final Object daemonLock = new Object();
private static volatile Bootstrap daemon = null;
// Bootstrap类的main方法
public static void main(String args[]) {
// 创建一个 Bootstrap 对象
synchronized (daemonLock) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
// 调用init方法初始化
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
// 根据启动参数,分别调用 Bootstrap 对象的不同方法
try {
// 默认参数为start
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
...
} else if (command.equals("stopd")) {
...
} 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")) {
...
} else if (command.equals("configtest")) {
...
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
反射实例化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");
// 通过catalinaLoader加载Catalina,反射实例化Catalina对象
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.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;
// 反射将sharedLoader设置为catalinaLoader的父类加载器,本文不做分析
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
// 将catalina实例引用赋值
catalinaDaemon = startupInstance;
}
load
与start
方法load方法:
private void load(String[] arguments) throws Exception {
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);
// 反射调用catalina的load方法,参数为null
method.invoke(catalinaDaemon, param);
}
start方法:
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
// 反射调用catalina的start方法,参数为null
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
conf/server.xml
文件StandardServer
的init
方法来初始化组件(下篇文章单独讲)public void load() {
// 如果已经加载则退出,默认false,下面会置为true
if (loaded) {
return;
}
loaded = true;
initDirs();
initNaming();
// 创建Digester对象,用来解析server.xml文件
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
// 加载conf目录下的server.xml文件
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
...
}
try {
inputSource.setByteStream(inputStream);
digester.push(this);
// 开始解析conf/server.xml文件
digester.parse(inputSource);
} catch (SAXParseException spe) {
...
}
// server和catalina之间建立关联
// Server接口实现类StandardServer是在解析server.xml文件时候创建
// 当时StandardServer对象set到Catalina
// 此时又将Catalinaset到StandardServer对象中
// 形成:你中有我,我中有你
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
...
// 初始化server,后面另开一篇单独讲
try {
getServer().init();
} catch (LifecycleException e) {
...
}
}
Digester对象解析server.xml文件
Server
和Service
是实现类StandardServer
和StandardService
组件的初始化
StandardServer
的start
方法来启动服务器(下篇文章单独讲)public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
// 无法启动服务器。未配置服务器实例
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// 调用server的start方法来启动服务器
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// 注册关闭钩子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
// 向addShutdownHook方法传入的Thread,其run方法即为自定义的shutdown时清理逻辑
Runtime.getRuntime().addShutdownHook(shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
// 进入等待状态
// 启动类Bootstrap默认调start方法设置await=true
if (await) {
// main线程等待,等待接收shutdown命令,接受到则跳出阻塞
await();
// 跳出阻塞,执行Server.stop();
stop();
}
}
Thread线程任务
,其run方法即为自定义的shutdown时清理逻辑
程序正常退出
,这发生在最后的非守护线程退出时,或者在调用exit(等同于 System.exit
)方法时^C
或发生系统事件,比如用户注销或系统关闭ps:对于非正常方式退出Java虚拟机,例如杀进程,系统断电等,这些情况下,shutdownHook不会被执行
protected class CatalinaShutdownHook extends Thread {
@Override
public void run() {
try {
if (getServer() != null) {
Catalina.this.stop();
}
} catch (Throwable ex) {
...
}
}
}
server.xml开头内容
public void await() {
getServer().await();
}
SHUTDOWN
命令后,退出阻塞,往下执行stop方法
// StandardServer类方法
private int port = 8005;
private String shutdown = "SHUTDOWN";
private volatile ServerSocket awaitSocket = null;
@Override
public void await() {
// shutdown端口配置为-2,启动完Server直接再终止Server
if( port == -2 ) {
return;
}
// 配置为-1,则不再监听shutdown端口
if( port==-1 ) {
try {
awaitThread = Thread.currentThread();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// continue and check the flag
}
}
} finally {
awaitThread = null;
}
return;
}
// 开启socket监听server.xml中的shutdown端口
// 创建socket服务端
try {
awaitSocket = new ServerSocket(port, 1,
InetAddress.getByName(address));
} catch (IOException e) {
return;
}
// 默认false,进入while循环
while (!stopAwait) {
ServerSocket serverSocket = awaitSocket;
if (serverSocket == null) {
break;
}
// Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder();
InputStream stream;
try {
// accept阻塞监听端口
socket = serverSocket.accept();
// 设置阻塞超时时间10秒,如果超时抛异常,catch捕捉到重新进入while循环
socket.setSoTimeout(10 * 1000);
stream = socket.getInputStream();
} catch (SocketTimeoutException ste) {
continue;
}
// 从流中读取字符串
...
// 如果读取到字符串命令是"SHUTDOWN"则,跳出循环,开始终止服务器
// shutdown变量是取server.xml中Server的shutdown属性
boolean match = command.toString().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
} else
log.warn("StandardServer.await: Invalid command '"
+ command.toString() + "' received");
}
}
public void stop() {
try {
if (useShutdownHook) {
// 移除shutdown钩子,这个stop方法会停止server,不需要钩子再次执行
Runtime.getRuntime().removeShutdownHook(shutdownHook);
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
// 调用Server的stop和destroy方法
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
&& LifecycleState.DESTROYED.compareTo(state) >= 0) {
// Nothing to do. stop() was already called
} else {
s.stop();
s.destroy();
}
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
}