LifeCycle 接口
上一篇文章中提到,Tomcat 中主要有这些重要的组件:
- Server:Server 容器代表了一个 tomcat 实例(Catalina 实例),可以包含一个或多个 Service 容器;
- Service:Service 是提供具体的对外服务的,一个 Service 容器中可以有多个 Connector 组件(监听不同的端口请求并处理)和一个 Servlet 容器(做具体的业务处理逻辑);
- Engine 和 Host:Engine 组件是 Servlet 容器的核心,它支持定义多个虚拟主机(Host),虚拟主机允许 Tomcat 引擎在一台机器上配置多个域名;
- Context:每个虚拟主机下支持部署多个 Web 应用,这就是我们所熟悉的上下文对象。 Context 是使用由 Servlet 规范中制定的 Web 应用程序的格式表示,不论是压缩后的 war 格式还是为压缩的目录结构;
- Wrapper:在一个 Context 中可以部署多个 Servlet,并且每个 Servlet 都会被一个 Wrapper 所包含。
这些组件是层层包含的。 Tomcat 通过定义 LifeCycle
接口来统一管理各组件的生命周期。我们来看一下 LifeCycle
接口中定义了以下方法:
public interface Lifecycle {
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
}
从 LifeCycle
这个类的继承关系中,我们也可以印证,如 Container
以及其包含的 Host
, Engine
, wrapper
和 Context
都实现了 LifeCycle
接口。
同样的,Service
和 Server
这两大组件也实现了 LifeCycle
接口,并分别被 StandardService
和 StandardServer
类实现。
启动入口分析
之前我们已经找到 Bootstrap
类是 Tomcat 程序的启动入口,那么我们是如何找到这个入口的呢?
首先,对于下载的 Tomcat Server,其启动入口是 bin/startup.sh
文件,他其实是启动了 catalina.sh
,并传入了 start
参数:
PRGDIR=`dirname "PRG"`
EXECUTABLE=catalina.sh
exec "PRGDIR"/"EXECUTABLE" start "@"
那么我们再进去看 catalina.sh
,搜索解析 start
参数的地方:
elif [ "1" = "start" ] ; then
# ...
if [ "1" = "-security" ] ; then
# eval <some_code>
else
eval _NOHUP ""_RUNJAVA"" ""CATALINA_LOGGING_CONFIG""LOGGING_MANAGER "JAVA_OPTS" "CATALINA_OPTS"
-DENDORSED_PROP=""JAVA_ENDORSED_DIRS""
-classpath ""CLASSPATH"" -Dcatalina.base=""CATALINA_BASE""
-Dcatalina.home=""CATALINA_HOME"" -Djava.io.tmpdir=""CATALINA_TMPDIR""
org.apache.catalina.startup.Bootstrap "@" start >> "CATALINA_OUT" 2>&1 "&"
fi
fi
所以 Tomcat 启动的过程就可以简化理解为:
java xxx.jar $SOME_OPTS org.apache.catalina.startup.Bootstrap
至此确认了 Tomcat 启动的入口就是这个 Bootstrap
类。
继续看 Bootstrap.main
方法:
public final class Bootstrap {
private Object catalinaDaemon = null;
private static volatile Bootstrap daemon = null;
public static void main(String args[]) {
synchronized (daemonLock) {
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;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
// ...
}
public void init() throws Exception {
initClassLoaders();
// 实例化 Catalina 对象 startupInstance
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
// Set the shared extensions class loader
String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
// 反射调用 startupInstance.setParentClassLoader(paramValues) 方法,设置类加载器
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
// 赋值给 catalinaDaemon
catalinaDaemon = startupInstance;
}
}
这段初始化逻辑,实际上是:
- 实例化一个
Catalina
对象并赋值给了 Bootstrap 中的成员变量catalinaDaemon
- 实例化一个
Bootstrap
对象并赋值给自己的成员变量daemon
接着往下看 Main 方法,这里指出了 Tomcat 启动的关键两个步骤:加载初始化阶段
和启动阶段
。
public final class Bootstrap {
public static void main(String args[]) {
if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
}
}
private void load(String[] arguments) throws Exception {
String methodName = "load";
Object param[];
Class<?> paramTypes[];
// 实际上是调用 Catalina.load()
Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
method.invoke(catalinaDaemon, param);
}
public void start() throws Exception {
if (catalinaDaemon == null) {
init();
}
// 实际上是调用 Catalina.start()
Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
method.invoke(catalinaDaemon, (Object [])null);
}
}
这里是典型的委派模式的应用。
加载初始化阶段
Server 初始化
先看 Catalina.load()
方法。
public class Catalina {
public void load() {
initDirs();
initNaming();
// Create and execute our Digester
Digester digester = createStartDigester();
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
// 解析 server.xml 配置文件
digester.parse(inputSource);
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
initStreams();
// 继续初始化下一层的 Server 对象
getServer().init();
}
}
此时初始化来到了 Server.init()
阶段,而这个接口定义在 LifeCycle.init()
中,这里 LifeCycle
对 Server
对象的生命周期进行了管理。
public abstract class LifecycleBase implements Lifecycle {
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
// 模板方法设计模式,定义抽象步骤顺序,由子类提供实现
try {
// 典型的状态模式的影子
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 核心初始化操作,这里是调用了 Server.initInternal()
initInternal();
// 典型的状态模式的影子
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}
}
public final class StandardServer extends LifecycleMBeanBase implements Server {
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// ...
// 继续初始化下一级所有的 Service 容器
// Service.init() 方法再次被 LifeCycle.init() 接管生命周期
for (Service service : services) {
service.init();
}
}
}
在 StandardServer
中,通过一个遍历继续初始化下一级所有的 Service
容器。
Service 初始化
老规矩,Service
容器的初始化也要先去 LifeCycle
报道,然后实现其 initInternal()
方法:
public class StandardService extends LifecycleMBeanBase implements Service {
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// 1. Servlet 容器引擎初始化
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 {
// 2. 遍历初始化所有的 Connector
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);
}
}
}
}
}
Engine 初始化
先看 Engine
对象的初始化逻辑。可以看到 StandardEngine
的初始化过程逻辑其实是在其父类 ContainerBase
中完成的。
public class StandardEngine extends ContainerBase implements Engine {
@Override
protected void initInternal() throws LifecycleException {
getRealm();
super.initInternal();
}
}
public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
}
可以看到 Engine
组件初始化阶段,其实就是创建了一个名叫 startStopExecutor
的线程池,这个线程池会在 Engine.start()
过程中使用,并对应多个 Host
实例。
Connector 初始化
Connector
的初始化,主要是调用了 ProtocolHandler.init()
方法。
public class Connector extends LifecycleMBeanBase {
protected final ProtocolHandler protocolHandler;
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// ...
// 初始化 protocolHandler
protocolHandler.init();
}
}
继续看 AbstractProtocol.init()
实现,其中调用了 Endpoint.init()
方法来初始化 Endpoint
对象。
public abstract class AbstractProtocol<S> implements ProtocolHandler, MBeanRegistration {
private final AbstractEndpoint<S> endpoint;
@Override
public void init() throws Exception {
// ...
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
// 初始化 Endpoint 对象
endpoint.init();
}
}
Endpoint
对象在初始化过程中调用了 bind()
虚方法,最终由 NioEndpoint
类来实现:
public abstract class AbstractEndpoint<S> {
public abstract void bind() throws Exception;
public void init() throws Exception {
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
// ...
}
}
public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
@Override
public void bind() throws Exception {
if (!getUseInheritedChannel()) {
// 获取 NIO Channel
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
// 绑定端口,但尚未 accept 客户端连接
serverSock.socket().bind(addr,getAcceptCount());
}
// ...
}
}
这里的 NioEndpoint
对象初始化过程中,获取了 NIO Channel 并完成了 serverSock.socket()
的端口绑定。
至此,Tomcat 启动过程中,主要组件的初始化逻辑基本完成了。