文章目录
叨叨
讲真,最开始源码调试几遍下来还是很懵了,尤其是组件初始化过程中不断去调用其他组件的init()方法,中间使用模板方法的设计模式,最终抽象方法交给哪个子类去实现有点绕。这个搞明白之后,start()方法的流程就和init()类似了。过程中涉及的组件经过初步梳理,再结合server.xml中的标签,基本可以猜个七七八八,最后结合Tomcat的架构设计图进行梳理修正。
这里先记录下启动流程的学习。Tomcat源码的下载及相关配置就不赘述了,网上有很多教程。
程序入口
java程序的执行入口是main()方法,根据官网的启动流程描述或者启动脚本中的内容,可以定位到一个类:
org.apache.catalina.startup.Bootstrap
进到这个类中直接搜索main()方法,看下main()方法主要分为两大块:
- 初始化bootstrap
进入init()方法可以看到是在做类加载器的初始化与设置- 加载与启动bootstrap
条件判断进入load()和start()的执行,按照猜想,在设置完类加载器后,框架启动的尿性就是读取并加载配置,初始化核心组件对象,然后启动框架。下面着重看下load和start两个方法。
加载load
流程简述
加载什么呢?无非就是:
- 配置信息的加载(毕竟我们还有个配置文件server.xml)
- 核心对象的加载
看下源码究竟做了什么:
Bootstrap.load()
通过反射调用Catalina.load()
Method method =
catalinaDaemon.getClass().getMethod("load", paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
System.out.println("******************* >>> 反射调用Catalina.load()");
method.invoke(catalinaDaemon, param);
Catalina.load(params)
首次看到load方法,直观感受:
- 有几个init开头的方法,初始化可能会用到的资源
- 创建了一个Digester对象,并解析输入源 digester.parse(inputSource)
- 执行getServer().init()
// inputSource来自于一个文件
digester.parse(inputSource);
// 文件正是 configFile = "conf/server.xml";
file = configFile();
所以digester对象是用来解析server.xml的。
getServer()返回一个Server对象,server何时被set进Catalina对象中的?
最后调用server.init()开始初始化流程。
init()模板方法
init()
方法是接口Lifecycle
中的方法,可以看到init()方法在LifecycleBase
类中实现,该类是一个抽象类,并且在init()
方法的执行逻辑中调用抽象方法initInternal()
,这不是妥妥的模板方法设计模式么。
所以这个initInternal
应该是在某个server实现类A中执行的,且类A需要继承LifecycleBase
或其子类。server只有一个实现类StandardServer
且满足上述条件。
LifecycleBase中init模板方法:
StandardServer中的实现:
// The set of Services associated with this Server.
private Service services[] = new Service[0];
protected void initInternal() throws LifecycleException {
// 调用父类
super.initInternal();
//...
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
//...
}
super.initInternal()方法:
最开始看到这里时还是有点乱的,梳理之后就发现这样的层次逻辑很清晰。通常我们使用模板方法> 有两层:
第一层:抽象模板类
第二层:继承了抽象类的具体子类Tomcat这里你可以理解成有三层:
第一层:抽象模板类
第二层:继承了抽象类的通用基础类LifecycleMBeanBase
第三层:继承了基础类的具体子类StandardXXX类模板类中定义了模板方法init和抽象方法initInternal;基础类中实现initInternal时做了公共逻辑的抽> 取(即注册组件),最后在具体组件类中进行具体组件的初始化逻辑。
看一下类的继承关系:
再看一下在service组件的initInternal方法中,调用了engine,executor,mapperListener,connector四个组件的init()方法:
在具体组件类的初始化过程initInternal()中,都是通过super.initInternal()调用通用的组件注册逻
辑,最后再执行组件自身的初始化逻辑。
service中四个组件的init概览:
engine.init();
1. Host组件的初始化并没有在此处执行,而是在start阶段进行的。(具体见start过程)
2. engine的初始化主要做了如下操作:
3. realm对象的处理
4. 完成engine组件的注册
executor.init();
完成executor组件的注册
mapperListener.init();
完成listener组件的注册
connector.init();
1. 创建coyote适配器
2. protocolHandler.init() --> abstractProtocol.init() --> endpoint.init()
init流程总结
两load:
bootstrap.load:反射调用Catalina.load();
catalina.load:创建xml解析器解析server.xml配置,调用server.init()开始初始化;
Lifecycle实现类与模板方法模式:
通过定义Lifecycle接口以及使用模板方法,规范并统一了组件类的初始化过程。
需要注意:
Host等组件的初始化是在start过程中进行的。(见下说明)
start()模板方法
流程简述
组件的start()方法与init的流程类似,也是通过Lifecycle接口配合使用模板方法的模式完成组件的启动。
boostrap.start通过反射调用Catalina.start()
Catalina.start
- 检查server实例是否存在
- getServer().start()启动server
start()模板方法
LifecycleBase中start模板方法:
public final synchronized void start() throws LifecycleException {
System.out.println("******************* >>> "+this.getClass().getName()+" 调用LifecycleBase.start");
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
// 检查组件状态,是否需要初始化。Host等主键的初始化就是在这里开始的
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// 调用抽象模板的具体子类
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}
protected abstract void startInternal() throws LifecycleException;
StandardServer中的实现:
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
service的start():
与init过程类似,在start过程中,同样也是同时对多个组件进行start()方法调用。
StandardEngine.start
直接调用父类ContainerBase.startInternal()方法,启动子组件和子容器;
子容器组件的启动通过线程池
StandardContext.startInternal
Host.start()特别说明
按照上面几个组件的加载机制以及server.xml中标签的层次结构,engine初始化时应该调用host的初始化方法,网上很多教程帖子的启动时序图也都是那样画的。这跟源码中的init流程是不符的
于是我开始在load阶段逐行阅读,始终找不到哪里调用了Host的init方法,最后在日志中找到答案:
启动流程之后细说,这里只需要知道:
- engine会将子标签(server.xml标签层级)对象遍历出来构建成可运行的线程丢到线程
- 这些子标签组件会调用自身的start()方法启动
- 组件启动时会检查组件状态,如果为NEW则会执行组件init方法