热加载
热加载是在不重启Tomcat的情况下,在Context- StandardContext
中重新加载应用的class。热加载是由一个专门的守护线程执行操作的,这个守护线程是ContainerBackgroundProcessor。
在StandardEngine启动时,会调用父类ContainerBase的threadStart()方法,就是在此方法中开启了ContainerBackgroundProcessor 线程!此方法代码如下:
protected void threadStart() {
if (thread != null)
return;
// StandardEngine初始化的构造方法会设置backgroundProcessorDelay的值为10
if (backgroundProcessorDelay <= 0)
return;
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
// 守护线程
thread.setDaemon(true);
thread.start();
}
ContainerBackgroundProcessor:
protected class ContainerBackgroundProcessor implements Runnable {
@Override
public void run() {
Throwable t = null;
String unexpectedDeathMessage = sm.getString(
"containerBase.backgroundProcess.unexpectedThreadDeath",
Thread.currentThread().getName());
try {
while (!threadDone) {
try {
// 每10s执行一次
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
// 调用processChildren
processChildren(ContainerBase.this);
}
}
} catch (RuntimeException|Error e) {
t = e;
throw e;
} finally {
if (!threadDone) {
log.error(unexpectedDeathMessage, t);
}
}
}
protected void processChildren(Container container) {
ClassLoader originalClassLoader = null;
try {
if (container instanceof Context) {
Loader loader = ((Context) container).getLoader();
// Loader will be null for FailedContext instances
if (loader == null) {
return;
}
// Ensure background processing for Contexts and Wrappers
// is performed under the web app's class loader
originalClassLoader = ((Context) container).bind(false, null);
}
// StanderContext重写了backgroundProcess(),就是在这个重写方法中进行了热加载
container.backgroundProcess();
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
// 递归调用processChildren
processChildren(children[i]);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("Exception invoking periodic operation: ", t);
} finally {
if (container instanceof Context) {
((Context) container).unbind(false, originalClassLoader);
}
}
}
}
StandardContext的backgroundProcess()方法:注意执行完之后还会调用父类ContainerBase的backgroundProcess()方法。
public void backgroundProcess() {
if (!getState().isAvailable())
return;
Loader loader = getLoader();
if (loader != null) {
try {
// 实现热加载
loader.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.loader", loader), e);
}
}
Manager manager = getManager();
if (manager != null) {
try {
manager.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.manager", manager),
e);
}
}
WebResourceRoot resources = getResources();
if (resources != null) {
try {
resources.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.resources",
resources), e);
}
}
InstanceManager instanceManager = getInstanceManager();
if (instanceManager instanceof DefaultInstanceManager) {
try {
((DefaultInstanceManager)instanceManager).backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.instanceManager",
resources), e);
}
}
// 调用父类ContainerBase的backgroundProcess()方法
super.backgroundProcess();
}
WebappLoader#backgroundProcess:
public void backgroundProcess() {
// 如果Context配置了reloadable =true,且文件已修改
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader
(WebappLoader.class.getClassLoader());
if (context != null) {
// 调用StandardContext的reload()进行热加载
context.reload();
}
} finally {
if (context != null && context.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
}
}
StandardContext#reload
public synchronized void reload() {
// 设置暂停,停止接收新的请求
// Stop accepting requests temporarily.
setPaused(true);
try {
// 调用stop方法,停止context
stop();
} catch (LifecycleException e) {
log.error(
sm.getString("standardContext.stoppingContext", getName()), e);
}
try {
// 调用start,重新启动context
start();
} catch (LifecycleException e) {
log.error(
sm.getString("standardContext.startingContext", getName()), e);
}
// 设置开启,重新接收请求
setPaused(false);
}