Tomcat源码初识一 Tomcat整理流程图
Tomcat源码初识二 用文字描述整体流程
Tomcat源码初识三 Tomcat如何实现热加载与热部署
Tomcat源码初识四 Tomcat如何打破双亲委派
Tomcat源码初识五 Tomcat如何处理HTTP请求
热部署流程如下:
- StandardEngine执行processChildren(child)方法时,一层层调用子类processChildren方法
- processChildren方法会执行container.backgroundProcess()方法
- StandardContext的backgroundProcess方法执行热加载
- 首先判断reloadable =true,然后判断缓存的文件时间与当前文件修改时间对比,如果修改调用reload方法
热加载流程如下:
- StandardEngine初始化时会修改backgroundProcessorDelay的值为10
- StandardEngine启动时,会创建一个线程,每次sleep 10秒钟(第一步的值)
- 调用StandardEngine容器的processChildren方法
- StandardEngine.processChildren()方法会调用子容器(StandardHost)的processChildren方法
- StandardHost.processChildren()方法会触发PERIODIC_EVENT事件
- 事件通知到HostConfig.lifecycleEvent方法,然后调用check方法
- 如果host的autoDeploy为ttrue,调用deployApps方法
- 加载类文件
热部署代码执行流程如下:
StandardEngine初始化时会修改backgroundProcessorDelay的值,代码如下
public StandardEngine() {
...
// 执行间隔,复制10,代表10秒钟检测一次
backgroundProcessorDelay = 10;
}
StandardEngine启动时,会调用threadStart()方法, 此方法代码如下
// 容器后台监控(热部署与热加载线程)
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
线程启动后,每隔10秒钟执行一次检测,调用子容器的processChildren
// 容器后台监控(热部署与热加载线程)
public void run() {
...
try {
while (!threadDone) {
try {
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
processChildren(ContainerBase.this);
}
}
} ...
}
protected void processChildren(Container container) {
...
for (Container child : children) {
if (child.getBackgroundProcessorDelay() <= 0) {
//实现热加载
processChildren(child);
}
}
...
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);.
}
protected void processChildren(Container container) {
try {
...
//后台监控(实现热加载)
container.backgroundProcess();
Container[] children = container.findChildren();
for (Container child : children) {
if (child.getBackgroundProcessorDelay() <= 0) {
processChildren(child);
}
}
}
...
}
子容器的processChildren方法中,会触发fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);这个方法会通知对应的监听,StandardEngine的子容器StandardHost对应的监听器HostConfig,代码如下
public void lifecycleEvent(LifecycleEvent event) {
...
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();
}
}
protected void check() {
//如果配置了热部署
if (host.getAutoDeploy()) {
...
deployApps();
}
}
deployApps方法会调用deployDescriptors、deployWARs、deployDirectories。deployDirectories方法如下
protected void deployDirectories(File appBase, String[] files) {
if (files == null)
return;
//获取StartStop线程池
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (String file : files) {
...
if (dir.isDirectory()) {
...
//多线程执行DeployDirectory
results.add(es.submit(new DeployDirectory(this, cn, dir)));
}
}
for (Future<?> result : results) {
try {
result.get();
} catch (Exception e) {
}
}
}
DeployDirectory类代码如下
public void run() {
config.deployDirectory(cn, dir);
}
//实际调用
protected void deployDirectory(ContextName cn, File dir) {
...
try {
if (deployThisXML && xml.exists()) {
...
} else if (!deployThisXML && xml.exists()) {
...
} else {
//通过反射创建StandardContext对象
context = (Context) Class.forName(contextClass).getConstructor().newInstance();
}
//创建context对象的监听对象ContextConfig
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
//设置监听
context.addLifecycleListener(listener);
...
//项目添加到host中(部署)
host.addChild(context);
} catch (Throwable t) {
...
} finally {
...
}
...
}
项目添加到host容器中,代码流程如下:
host.addChild(context)->StandardHost.addChild()->ContainerBase.addChild()->ContainerBase.addChildInternal(child)->child.start()->LifecycleBase.startInternal()->StandardContext.startInternal()
protected synchronized void startInternal() throws LifecycleException {
...
// 在work文件夹中添加
postWorkDirectory();
...
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader();
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
// Initialize character set mapper
getCharsetMapper();
// Validate required extensions
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error(sm.getString("standardContext.extensionValidationError"), ioe);
dependencyCheck = false;
}
if (!dependencyCheck) {
// do not make application available if dependency check fails
ok = false;
}
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener();
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Standard container startup
if (log.isDebugEnabled())
log.debug("Processing standard container startup");
// Binding thread
ClassLoader oldCCL = bindThread();
try {
if (ok) {
// 初始化
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}
...
if (ok) {
//加载listener
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
...
if (ok) {
//加载filter
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// 加载servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
//开启新线程热加载
super.threadStart();
}
....
}
热加载代码执行流程如下:
StandardEngine执行processChildren(child)方法时,一层层调用子类processChildren方法,processChildren方法会执行container.backgroundProcess()方法,执行代码如下:
public void backgroundProcess() {
...
Loader loader = getLoader();
if (loader != null) {
try {
//实现热加载
loader.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.loader", loader), e);
}
}
...
}
public void backgroundProcess() {
//如果Context配置了reloadable =true,判断是否有文件修改
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader
(WebappLoader.class.getClassLoader());
if (context != null) {
//重新加载context
context.reload();
}
} finally {
if (context != null && context.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
}
}
//判断文件是否修改过
public boolean modified() {
//循环所有文件,判断修改时间
for (Entry<String,ResourceEntry> entry : resourceEntries.entrySet()) {
long cachedLastModified = entry.getValue().lastModified;
long lastModified = resources.getClassLoaderResource(
entry.getKey()).getLastModified();
if (lastModified != cachedLastModified) {
if( log.isDebugEnabled() )
log.debug(sm.getString("webappClassLoader.resourceModified",
entry.getKey(),
new Date(cachedLastModified),
new Date(lastModified)));
return true;
}
}
...
//循环所有jar
for (WebResource jar : jars) {
if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
jarCount++;
Long recordedLastModified = jarModificationTimes.get(jar.getName());
if (recordedLastModified == null) {
// Jar has been added
log.info(sm.getString("webappClassLoader.jarsAdded",
resources.getContext().getName()));
return true;
}
if (recordedLastModified.longValue() != jar.getLastModified()) {
// Jar has been changed
log.info(sm.getString("webappClassLoader.jarsModified",
resources.getContext().getName()));
return true;
}
}
}
if (jarCount < jarModificationTimes.size()){
log.info(sm.getString("webappClassLoader.jarsRemoved",
resources.getContext().getName()));
return true;
}
...
return false;
}
Tomcat源码初识一 Tomcat整理流程图
Tomcat源码初识二 用文字描述整体流程
Tomcat源码初识三 Tomcat如何实现热加载与热部署
Tomcat源码初识四 Tomcat如何打破双亲委派
Tomcat源码初识五 Tomcat如何处理HTTP请求