在上文揭开tomcat神秘的面纱之bootstrap加载中,本菜鸟分析了bootstrap最终会经过初始化,加载,启动三个步骤。接着来分析启动过程。
tomcat的启动过程.png在
bootstrap
的main
方法中,加载完成之后就会调用catalina
的start
方法,启动容器,而在catalina
的start
方法中,实质是调用server
的start
方法。
//Bootstrap.java
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);//catalina的start方法。
}
Catalina.java
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
return;
}
getServer().start();//拿到的是StandardServer
}
而在StandardServer
的start
方法中,实质是调用StandService
的start
方法,最终调用engine
的start
方法。
//StandardServer.java
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();//这里调用StandService的start方法。
}
}
}
//StandardService.java
@Override
protected void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
synchronized (connectorsLock) {
for (Connector connector: connectors) {
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
关于这里为啥不是start
方法,而是startInternal
,这是因为他们共同继承于LifecycleBase
类,该类中,会调用startInternal
方法,调用start
方法,实质最终都会调用startInternal
。
//LifecycleBase.java
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
return;
}
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);
}
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();//子类实现该接口
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
}
而在StandardEngine
的start
方法中,实质是通过多线程,创建StartChild
方法,启动StandardHost
,
//ContainerBase.java
protected synchronized void startInternal() throws LifecycleException {
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
//这里提交任务,就是启动StandardHost
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
result.get();
}
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
setState(LifecycleState.STARTING);
threadStart();
}
而在StartChild
方法中,只是调用子容器的start
方法。此处为StandardHost
。
StartChild.java
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}
而在StandardHost
调用start
方法的时候,会广播一个start
事件,最终会被HostConfig
接收,并处理,调用HostConfig
的start
方法,最终会将webapp
文件夹下的每一个war
,或者文件夹,单独作为一个任务DeployWar
,提交到线程池,去启动这个Context
,而DeployWar
这个任务,实质还是调用HostConfig
的deployWAR
方法,这里本菜鸟就以一个war
包为例,来介绍一下。
public void start() {
ObjectName hostON = host.getObjectName();
oname = new ObjectName
(hostON.getDomain() + ":type=Deployer,host=" + host.getName());
Registry.getRegistry(null, null).registerComponent
(this, oname, this.getClass().getName());
if (!host.getAppBaseFile().isDirectory()) {
host.setDeployOnStartup(false);
host.setAutoDeploy(false);
}
if (host.getDeployOnStartup())
deployApps();//发布app
}
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);//发布war包服务
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);//发布文件夹的包服务
}
protected void deployWARs(File appBase, String[] files) {
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
if (files[i].equalsIgnoreCase("META-INF"))
continue;
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
File war = new File(appBase, files[i]);
if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
war.isFile() && !invalidWars.contains(files[i]) ) {
ContextName cn = new ContextName(files[i], true);
if (isServiced(cn.getName())) {
continue;
}
if (deploymentExists(cn.getName())) {
DeployedApplication app = deployed.get(cn.getName());
boolean unpackWAR = unpackWARs;
if (unpackWAR && host.findChild(cn.getName()) instanceof StandardContext) {
unpackWAR = ((StandardContext) host.findChild(cn.getName())).getUnpackWAR();
}
if (!unpackWAR && app != null) {
File dir = new File(appBase, cn.getBaseName());
if (dir.exists()) {
app.loggedDirWarning = true;
} else {
app.loggedDirWarning = false;
}
}
continue;
}
if (!validateContextPath(appBase, cn.getBaseName())) {
invalidWars.add(files[i]);
continue;
}
results.add(es.submit(new DeployWar(this, cn, war)));
//重点在这里,提交一个DeployWar任务。
}
}
for (Future<?> result : results) {
result.get();
}
}
private static class DeployWar implements Runnable {
private HostConfig config;
private ContextName cn;
private File war;
public DeployWar(HostConfig config, ContextName cn, File war) {
this.config = config;
this.cn = cn;
this.war = war;
}
@Override
public void run() {
config.deployWAR(cn, war);
//HostConfig的deployWAR方法。
}
}
而在HostConfig
的deployWAR
方法中,会创建Context
,然后将该Context
添加到Host
过程中(addChild方法),会启动Context
。
protected void deployWAR(ContextName cn, File war) {
File xml = new File(host.getAppBaseFile(),
cn.getBaseName() + "/" + Constants.ApplicationContextXml);
File warTracker = new File(host.getAppBaseFile(), cn.getBaseName() + Constants.WarTracker);
boolean xmlInWar = false;
try (JarFile jar = new JarFile(war)) {
JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
if (entry != null) {
xmlInWar = true;
}
} catch (IOException e) {
/* Ignore */
}
boolean useXml = false;
if (xml.exists() && unpackWARs &&
(!warTracker.exists() || warTracker.lastModified() == war.lastModified())) {
useXml = true;
}
Context context = null;
//初始化Context
boolean deployThisXML = isDeployThisXML(war, cn);
if (deployThisXML && useXml && !copyXML) {
synchronized (digesterLock) {
context = (Context) digester.parse(xml);
}
context.setConfigFile(xml.toURI().toURL());
} else if (deployThisXML && xmlInWar) {
synchronized (digesterLock) {
try (JarFile jar = new JarFile(war)) {
JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
context = (Context) digester.parse(istream);
}
} else if (!deployThisXML && xmlInWar) {
} else {
context = (Context) Class.forName(contextClass).getConstructor().newInstance();
}
boolean copyThisXml = false;
if (deployThisXML) {
if (host instanceof StandardHost) {
copyThisXml = ((StandardHost) host).isCopyXML();
}
if (!copyThisXml && context instanceof StandardContext) {
copyThisXml = ((StandardContext) context).getCopyXML();
}
if (xmlInWar && copyThisXml) {
xml = new File(host.getConfigBaseFile(),
cn.getBaseName() + ".xml");
try (JarFile jar = new JarFile(war)) {
JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
try (InputStream istream = jar.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(xml);
BufferedOutputStream ostream = new BufferedOutputStream(fos, 1024)) {
byte buffer[] = new byte[1024];
while (true) {
int n = istream.read(buffer);
if (n < 0) {
break;
}
ostream.write(buffer, 0, n);
}
ostream.flush();
}
} catch (IOException e) {
}
}
}
DeployedApplication deployedApp = new DeployedApplication(cn.getName(),
xml.exists() && deployThisXML && copyThisXml);
long startTime = 0;
try{
deployedApp.redeployResources.put
(war.getAbsolutePath(), Long.valueOf(war.lastModified()));
if (deployThisXML && xml.exists() && copyThisXml) {
deployedApp.redeployResources.put(xml.getAbsolutePath(),
Long.valueOf(xml.lastModified()));
} else {
deployedApp.redeployResources.put(
(new File(host.getConfigBaseFile(),
cn.getBaseName() + ".xml")).getAbsolutePath(),
Long.valueOf(0));
}
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() + ".war");
host.addChild(context);//添加子Context
} catch (Throwable t) {
} finally {
boolean unpackWAR = unpackWARs;
if (unpackWAR && context instanceof StandardContext) {
unpackWAR = ((StandardContext) context).getUnpackWAR();
}
if (unpackWAR && context.getDocBase() != null) {
File docBase = new File(host.getAppBaseFile(), cn.getBaseName());
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
Long.valueOf(docBase.lastModified()));
addWatchedResources(deployedApp, docBase.getAbsolutePath(),
context);
if (deployThisXML && !copyThisXml && (xmlInWar || xml.exists())) {
deployedApp.redeployResources.put(xml.getAbsolutePath(),
Long.valueOf(xml.lastModified()));
}
} else {
addWatchedResources(deployedApp, null, context);
}
addGlobalRedeployResources(deployedApp);
}
deployed.put(cn.getName(), deployedApp);
}
private void addChildInternal(Container child) {
if( log.isDebugEnabled() )
log.debug("Add child " + child + " " + this);
synchronized(children) {
if (children.get(child.getName()) != null)
throw new IllegalArgumentException("addChild: Child name '" +
child.getName() +
"' is not unique");
child.setParent(this); // May throw IAE
children.put(child.getName(), 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) {
child.start();//启动Context
}
} catch (LifecycleException e) {
log.error("ContainerBase.addChild: start: ", e);
throw new IllegalStateException("ContainerBase.addChild: start: " + e);
} finally {
fireContainerEvent(ADD_CHILD_EVENT, child);
}
}
//ContainerBase.java 此处为StandardHost.addChild方法
//addChild
private void addChildInternal(Container child) {
synchronized(children) {
child.setParent(this);
children.put(child.getName(), child);
}
try {
if ((getState().isAvailable() ||
LifecycleState.STARTING_PREP.equals(getState())) &&
startChildren) {
child.start();//启动context
}
} finally {
fireContainerEvent(ADD_CHILD_EVENT, child);
}
}
关于Context初始化的内容,后面继续更新。
网友评论