Engine
全类名:org.apache.catalina.core.StandardEngine;
由类依赖图可知,Engine是一个生命周期的类,实现了Lifecycle,也是一个容器,实现了Container。
Container有4个子接口,实现了该接口的也都是容器。我们继续从Engine开始来分析它们之间的关系。
Engine
实现类:org.apache.catalina.core.StandardEngine;
创建
/**
* Create a new StandardEngine component with the default basic Valve.
*/
public StandardEngine() {
super();
// 管道增加引擎阀
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
// By default, the engine will hold the reloading thread
backgroundProcessorDelay = 10;
}
主要是给StandardEngine的管道pipeline添加了引擎阀StandardEngineValve。
init
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
没有核心逻辑,跳过。
start
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Log our server identification information
if (log.isInfoEnabled()) {
log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
}
// Standard container startup 容器启动
super.startInternal();
}
看注释可知,容器启动在父类ContainerBase方法startInternal();
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// 省略
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
// 启动子类
results.add(startStopExecutor.submit(new StartChild(child)));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
try {
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any
// 启动管道阀
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 调用监听事件
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
这是容器的一个模板方法,核心逻辑:
1)找到子类,并启动
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
// 启动子类
results.add(startStopExecutor.submit(new StartChild(child)));
}
2)启动管道
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
3)调用监听事件
setState(LifecycleState.STARTING);
// 后面会调LifecycleBase的该方法
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
Engine的启动调用图:
Engine的启动过程中pileline和listener并没有核心逻辑,核心逻辑在启动child上。另外图中pipeline省略了其他的像日志打印的Valve。
主要会执行StandardHost.start();
Host
StandardHost也是一个容器,启动调用图:
Host的启动核心调用逻辑在HostConfig。
/**
* Process the START event for an associated Host.
*
* @param event The lifecycle event that has occurred
*/
@Override
public void lifecycleEvent(LifecycleEvent event) {
//省略
// Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
/**
* Process a "start" event for this Host.
*/
public void start() {
// 省略
if (host.getDeployOnStartup()) {
deployApps();
}
}
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
*/
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
/**
* Deploy exploded webapps.
* @param appBase The base path for applications
* @param files The exploded webapps that should be deployed
*/
protected void deployDirectories(File appBase, String[] files) {
if (files == null) {
return;
}
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (String file : files) {
if (file.equalsIgnoreCase("META-INF")) {
continue;
}
if (file.equalsIgnoreCase("WEB-INF")) {
continue;
}
File dir = new File(appBase, file);
if (dir.isDirectory()) {
ContextName cn = new ContextName(file, false);
if (tryAddServiced(cn.getName())) {
try {
if (deploymentExists(cn.getName())) {
removeServiced(cn.getName());
continue;
}
// DeployDirectory will call removeServiced 把部署任务扔到一个线程池里执行
results.add(es.submit(new DeployDirectory(this, cn, dir)));
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
removeServiced(cn.getName());
throw t;
}
}
}
}
for (Future<?> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("hostConfig.deployDir.threaded.error"), e);
}
}
}
整理下上面方法的调用步骤:
启动一个线程DeployDirectory,异步方式调用HostConfig的deployDirectory()方法
/**
* Deploy exploded webapp.
* <p>
* Note: It is expected that the caller has successfully added the app
* to servicedSet before calling this method.
*
* @param cn The context name
* @param dir The path to the root folder of the webapp
*/
protected void deployDirectory(ContextName cn, File dir) {
// 非启动核心流程,省略
Context context = null;
context = (Context) Class.forName(contextClass).getConstructor().newInstance();
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName());
// 虚拟主机添加context,最终会调用父类方法ContainerBase.addChildInternal(Container child)
host.addChild(context);
// 非启动核心流程,省略
}
ContainerBase的addChildInternal方法:
private void addChildInternal(Container child) {
// 非启动核心流程,省略
// Start child
// Don't do this inside sync block - start can be a slow process and
// locking the children object can cause problems elsewhere
try {
if ((getState().isAvailable() ||
LifecycleState.STARTING_PREP.equals(getState())) &&
startChildren) {
// 调用context启动方法
child.start();
}
} catch (LifecycleException e) {
throw new IllegalStateException(sm.getString("containerBase.child.start"), e);
}
}
child.start() 调用的就是StandContext的start()方法
Context
Context也是一个容器,实现类:org.apache.catalina.core.StandardContext
StandardContext启动调用图:
分别来分析下Context调用:
第一步:创建WebappLoader
会创建一个自定义的类加载器,回看之前分享的Bootsrap创建的3个类加载器,加上在这里创建的,tomcat的类加载器如图
第二步:执行ContextConfig
会加载解析web.xml,创建wrapper,每一个wrapper就对应一个Servlet.
调用链如图:
第三步:循环判断child是否已启动,未启动执行child的start,也即执行StandardWrapper.start,没有核心逻辑。
第四步:执行pipeline.start,没有核心逻辑。
第五步:创建Manager StandardManager,该类用于管理会话Session。
第六步:创建InstanceManager,默认实现类DefaultInstanceManager,该类用于实例化对象,包括Listener、Filter、Servlet.
第七步:调用方法listenerStart()、filterStart()、loadOnStartup(findChildren()),这几个方法分别为创建并设置监听、过滤器、以及加载和初始设置了load-on-startup的Servlet。
/**
* Load and initialize all servlets marked "load on startup" in the
* web application deployment descriptor.
*
* @param children Array of wrappers for all currently defined
* servlets (including those not declared load on startup)
* @return <code>true</code> if load on startup was considered successful
*/
public boolean loadOnStartup(Container children[]) {
// Collect "load on startup" servlets that need to be initialized
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
for (Container child : children) {
Wrapper wrapper = (Wrapper) child;
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0) {
continue;
}
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(wrapper);
}
// Load the collected "load on startup" servlets
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
wrapper.load();
} catch (ServletException e) {
getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
// unless failCtxIfServletStartFails="true" is specified
if(getComputedFailCtxIfServletStartFails()) {
return false;
}
}
}
}
return true;
}
执行完这几步,Context启动完成。
根据以上可知容器之间的关系图: