HostConfig.deployApps() //在监听到start事件类型,也就是StandardHost调用startInternal protected void deployApps() { File appBase = host.getAppBaseFile(); //这个值是在触发before_start时间时生成的,默认是tomcat安装目录+engine名+host名 File configBase = host.getConfigBaseFile(); //获取host上配置的webapp下的所有文件,默认是webapps目录下的所有文件 String[] filteredAppPaths = filterAppPaths(appBase.list()); // Deploy XML descriptors from configBase 发布xml描述文件 deployDescriptors(configBase, configBase.list()); // Deploy WARs deployWARs(appBase, filteredAppPaths); // Deploy expanded folders deployDirectories(appBase, filteredAppPaths); } deployDirectories protected void deployDirectories(File appBase, String[] files) { if (files == null) return; 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 dir = new File(appBase, files[i]); if (dir.isDirectory()) { ContextName cn = new ContextName(files[i], false); if (isServiced(cn.getName()) || deploymentExists(cn.getName())) continue; results.add(es.submit(new DeployDirectory(this, cn, dir))); } } for (Future<?> result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString( "hostConfig.deployDir.threaded.error"), e); } } } HostConfig.deployDirectory(cn, dir); protected void deployDirectory(ContextName cn, File dir) { long startTime = 0; // Deploy the application in this directory if( log.isInfoEnabled() ) { startTime = System.currentTimeMillis(); log.info(sm.getString("hostConfig.deployDir", dir.getAbsolutePath())); } Context context = null; //Constants.ApplicationContextXml == META-INF/context.xml File xml = new File(dir, Constants.ApplicationContextXml); //当允许copyxml的时候会使用到 File xmlCopy = new File(host.getConfigBaseFile(), cn.getBaseName() + ".xml"); DeployedApplication deployedApp; boolean copyThisXml = isCopyXML(); boolean deployThisXML = isDeployThisXML(dir, cn); try { if (deployThisXML && xml.exists()) { synchronized (digesterLock) { try { context = (Context) digester.parse(xml); } catch (Exception e) { log.error(sm.getString( "hostConfig.deployDescriptor.error", xml), e); context = new FailedContext(); } finally { digester.reset(); if (context == null) { context = new FailedContext(); } } } //这里有点疑问,为什么host配置的copyThisXml为false的时候才允许context进行覆盖? if (copyThisXml == false && context instanceof StandardContext) { // Host is using default value. Context may override it. copyThisXml = ((StandardContext) context).getCopyXML(); } //当允许copy时,进行复制,并把配置文件设置为新的目录 if (copyThisXml) { Files.copy(xml.toPath(), xmlCopy.toPath()); context.setConfigFile(xmlCopy.toURI().toURL()); } else { context.setConfigFile(xml.toURI().toURL()); } } else if (!deployThisXML && xml.exists()) {//如果不允许发布这个配置文件并且对应的xml存在的话,构造失败context,why???即使存在context.xml也可以构造一个默认的context // Block deployment as META-INF/context.xml may contain security // configuration necessary for a secure deployment. log.error(sm.getString("hostConfig.deployDescriptor.blocked", cn.getPath(), xml, xmlCopy)); context = new FailedContext(); } else { context = (Context) Class.forName(contextClass).getConstructor().newInstance(); } //下面的步骤和部署描述文件和部署war是一样的。不再赘述 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()); host.addChild(context); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(sm.getString("hostConfig.deployDir.error", dir.getAbsolutePath()), t); } finally { deployedApp = new DeployedApplication(cn.getName(), xml.exists() && deployThisXML && copyThisXml); // Fake re-deploy resource to detect if a WAR is added at a later // point deployedApp.redeployResources.put(dir.getAbsolutePath() + ".war", Long.valueOf(0)); deployedApp.redeployResources.put(dir.getAbsolutePath(), Long.valueOf(dir.lastModified())); if (deployThisXML && xml.exists()) { if (copyThisXml) { deployedApp.redeployResources.put( xmlCopy.getAbsolutePath(), Long.valueOf(xmlCopy.lastModified())); } else { deployedApp.redeployResources.put( xml.getAbsolutePath(), Long.valueOf(xml.lastModified())); // Fake re-deploy resource to detect if a context.xml file is // added at a later point deployedApp.redeployResources.put( xmlCopy.getAbsolutePath(), Long.valueOf(0)); } } else { // Fake re-deploy resource to detect if a context.xml file is // added at a later point deployedApp.redeployResources.put( xmlCopy.getAbsolutePath(), Long.valueOf(0)); if (!xml.exists()) { deployedApp.redeployResources.put( xml.getAbsolutePath(), Long.valueOf(0)); } } addWatchedResources(deployedApp, dir.getAbsolutePath(), context); // Add the global redeploy resources (which are never deleted) at // the end so they don't interfere with the deletion process addGlobalRedeployResources(deployedApp); } deployed.put(cn.getName(), deployedApp); if( log.isInfoEnabled() ) { log.info(sm.getString("hostConfig.deployDir.finished", dir.getAbsolutePath(), Long.valueOf(System.currentTimeMillis() - startTime))); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?