在前面我们了解了一个tomcat是如何构建起来,就像是一个机器人,我使用各种组件组装起来,形成一个长得像人的机器人,现在是时候按下电源按钮,检验我们的成果了。电源按下,机器人开口说了声正在初始化。。。。。。
服务初始化
getServer().init();
首先来看下StandardServer的类图
其中Lifecycle接口定义每个生命周期的常量
/**
* The LifecycleEvent type for the "component before init" event.
*/
public static final String BEFORE_INIT_EVENT = "before_init";
/**
* The LifecycleEvent type for the "component after init" event.
*/
public static final String AFTER_INIT_EVENT = "after_init";
/**
* The LifecycleEvent type for the "component start" event.
*/
public static final String START_EVENT = "start";
/**
* The LifecycleEvent type for the "component before start" event.
*/
public static final String BEFORE_START_EVENT = "before_start";
/**
* The LifecycleEvent type for the "component after start" event.
*/
public static final String AFTER_START_EVENT = "after_start";
/**
* The LifecycleEvent type for the "component stop" event.
*/
public static final String STOP_EVENT = "stop";
/**
* The LifecycleEvent type for the "component before stop" event.
*/
public static final String BEFORE_STOP_EVENT = "before_stop";
/**
* The LifecycleEvent type for the "component after stop" event.
*/
public static final String AFTER_STOP_EVENT = "after_stop";
/**
* The LifecycleEvent type for the "component after destroy" event.
*/
public static final String AFTER_DESTROY_EVENT = "after_destroy";
/**
* The LifecycleEvent type for the "component before destroy" event.
*/
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
/**
* The LifecycleEvent type for the "periodic" event.
*/
public static final String PERIODIC_EVENT = "periodic";
/**
* The LifecycleEvent type for the "configure_start" event. Used by those
* components that use a separate component to perform configuration and
* need to signal when configuration should be performed - usually after
* {@link #BEFORE_START_EVENT} and before {@link #START_EVENT}.
*/
public static final String CONFIGURE_START_EVENT = "configure_start";
/**
* The LifecycleEvent type for the "configure_stop" event. Used by those
* components that use a separate component to perform configuration and
* need to signal when de-configuration should be performed - usually after
* {@link #STOP_EVENT} and before {@link #AFTER_STOP_EVENT}.
*/
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
- 抽象LifecycleBase实现了Lifecycle接口,使用模板设计模式将一些重复性的动作封装在抽象类中,将具体的不同实现通过抽象方法的方式交由子类去实现
- LifecycleMBeanBase 主要是先MBean的内容,这里不对MBean做过多的分析
- StandardServer 一个具体的生命周期实现者
首先我们来看看Sever的init方法,这个init方法定义在LifecycleBese这个模板类中
public final synchronized void init() throws LifecycleException {
//如果初始化的时候声明周期不是NEW,那么说已经初始化过了,再次初始化就会报错
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
//将当前状态设置为正在初始化,并且调用感兴趣的生命周期监听器
setStateInternal(LifecycleState.INITIALIZING, null, false);
//这里面仅仅是构建出mbeanserver,并将自己注册到mbeanserver中进行管理
initInternal();
//初始化完成后就设置为初始化完成,并且调用感兴趣的生命周期监听器
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
调用生命周期监听器(LifecycleBase.fireLifecycleEvent)
protected void org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
//如果监听器感兴趣,那么就会进行处理
listener.lifecycleEvent(event);
}
}
StandardServer对初始化的具体实现
protected void initInternal() throws LifecycleException {
super.initInternal();
//。。。。。。省略MBean的注册代码
// Register the naming resources
//全局命名资源初始化,它除了将全局资源中的各个资源进行mbean管理
//之外没有做其他的事情
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
//获取Catalina的父类加载器,从前面来看这个类加载器是shareLoader加载
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
//加载jar中Manifest信息
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
// Initialize our defined Services 这里的service是解析xml的时候添加进来的
for (int i = 0; i < services.length; i++) {
//初始化Service
services[i].init();
}
}
StandardService.init()
首先我们看到StandardService的类图
StandardService的类图和StandardServer非常相似,仅仅不同的是实现的其中一个是Service,所以对于父类的初始化方法无非就是注册到mbean,所以我们直接看initInternal方法
protected void initInternal() throws LifecycleException {
super.initInternal();
//调用engine的初始化方法
if (engine != null) {
engine.init();
}
// Initialize any Executors
//初始化线程池,这里的线程池我们可以从前面解析config.xml中可以看到,tomcat创建的线程池是StandardThreadExecutor
//其实这个类和Service与Server一样除了实现的对应的Executor这个接口不同之外,其他都是一样的
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
//设置域名
((JmxEnabled) executor).setDomain(getDomain());
}
//StandardThreadExecutor,这里没有做其他的事情,就是调用了父类的init方法(注册到mbean)
executor.init();
}
// Initialize mapper listener
//注册自己到mbeanserver
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
//注册到mbean
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);
}
}
}
}
MapperListener类图
MapperListener只是进行了mbean服务的注册
接下来看下Connector的初始化
protected void initInternal() throws LifecycleException {
//注册mbean
super.initInternal();
// Initialize adapter
//创建CoyoteAdapter
adapter = new CoyoteAdapter(this);
//将适配器设置给协议处理器
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
//设置解析方法集合
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
//是否支持apr,native的调用,tomcat中一共有三种运行模式,分别是:bio,nio,apr
//apr是从操作系统级别解决异步IO问题,大幅度提高服务器的并发处理性能,也是Tomcat生产环境运行的首选方式
if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
//如果是apr模式,并且开启了https,那么设置ssl的处理实现,这里也将不会去分析ssl的实现
//况且我也不懂
if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
protocolHandler instanceof AbstractHttp11JsseProtocol) {
AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
(AbstractHttp11JsseProtocol<?>) protocolHandler;
if (jsseProtocolHandler.isSSLEnabled() &&
jsseProtocolHandler.getSslImplementationName() == null) {
// OpenSSL is compatible with the JSSE configuration, so use it if APR is available
jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
}
}
try {
//初始化协议处理器
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
以下为Connector的类图
protocolHandler.init()
我先来看下ProtocolHandler相关的类图
ProtocolHandler顾名思义就是用于处理通信协议的,一般我们使用的通信协议是http,所以一般在tomcat的config.xml配置文件中Connector中我们指定的协议都是HTTP1.1之类的,其对应的协议处理器为Http11NioProtocol
AbstractEndpoint 抽象端口,用于接收连接的类
在MapperListener进行初始化前,其实先初始化了Engine,之所以放到这,是因为从Engine开始属于容器的范畴,我认为放在一起分析会比较集中,不至于分散,一下为容器的大致类图
engine.init()
protected void initInternal() throws LifecycleException {
//确保存在Realm,如果没有会创建一个NullRealm
getRealm();
//调用父类的initInternal,对于容器来说调用的都是ContainerBase的initInternal方法
super.initInternal();
}
ContainerBase.initInternal()
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);
//LifecyleBase.initInternal()注册到mbeanServer
super.initInternal();
}
到这里,我们看到了Server.init->GlobalNamingResources.init->Service.init->Engine.init->Executor.init->MapperListener.init->Connector.init->ProtoHandler.init这些组件的初始化,但是我们在前面章节的tomcat架构图中可以看到还有Host,Context没有初始化,那么他们是怎么初始化的呢?还记得我们在第二节的Catalina创建Digester时创建StandardEngine使用的是RuleSet,其中添加了一个创建EngineConfig生命周期监听器的规则。相同的Host,Config都是这么干的,以下是他们的类图
EngineConfig
public void lifecycleEvent(LifecycleEvent event) {
// Identify the engine we are associated with
try {
engine = (Engine) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("engineConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
//处理Engine的启动事件
if (event.getType().equals(Lifecycle.START_EVENT))
start();
//处理Engine的停止事件
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop();
}
通过上面的生命周期监听器可以看出这个监听器只对启动事件和关闭事件感兴趣,似乎没有对Engine的子容器host进行初始化啊。难道host的初始化是在start中执行的,为了确定是否是这样,我们可以在host的初始化方法中打断点调试,最终确认我们的猜想是正确的。那这里就有一个问题了,为什么不直接初始化,偏要到Engine启动的时候初始化呢???我们带这个问题开始启动篇。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?