Tomcat 源码阅读(七)Tomcat加载web项目
来源:互联网 发布:电子竞技数据分析 编辑:程序博客网 时间:2024/05/23 20:07
坚持一下,把源码看完,勤奋一点,不要在懒惰了,你已经落下别人很多了
本文主要介绍Tomcat 是如何加载webapps下的项目
一、server.xml
在server.xml中有如下关于Host配置的一段代码
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">Host 的appBase:指定了存放web项目的文件夹 是webapps
二、加载流程
1、Digester解析server.xml
Catalina.createStartDigester 有如下一部分代码:
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
HostRuleSet的addRuleInstances中有如下代码
digester.addRule(prefix + "Host", new LifecycleListenerRule ("org.apache.catalina.startup.HostConfig", "hostConfigClass"));
digester解析Host配置信息的时候定义了这么一条规则, LifecycleListenerRule,
查看该规则的构造方法
public LifecycleListenerRule(String listenerClass, String attributeName) { this.listenerClass = listenerClass; this.attributeName = attributeName; }
也就是传入构造方法的两个参数一个代表了listenerClass的类名称,一个表示属性名称, LifecycleListenerRule的begin方法
public void begin(String namespace, String name, Attributes attributes) throws Exception { Container c = (Container) digester.peek(); Container p = null; Object obj = digester.peek(1); if (obj instanceof Container) { p = (Container) obj; } String className = null; // Check the container for the specified attribute if (attributeName != null) { String value = attributes.getValue(attributeName); if (value != null) className = value; } // Check the container's parent for the specified attribute if (p != null && className == null) { String configClass = (String) IntrospectionUtils.getProperty(p, attributeName); if (configClass != null && configClass.length() > 0) { className = configClass; } } // Use the default if (className == null) { className = listenerClass; } // Instantiate a new LifecycleListener implementation object Class<?> clazz = Class.forName(className); LifecycleListener listener = (LifecycleListener) clazz.newInstance(); // Add this LifecycleListener to our associated component c.addLifecycleListener(listener); }
最终可以确定Container c 指定的是Host对象 StandardHost类的实例
listener是 org.apache.catalina.startup.HostConfig 对象的实例,
通过调用 c.addLifecycleListener(listener); 将该listener 与StandardHost进行绑定。
2、org.apache.catalina.startup.HostConfig
当Tomcat启动的时候,最终加载webapps文件夹下的项目最终交由org.apache.catalina.startup.HostConfig 来处理。
HostConfig 实现了LifecycleListener接口,对接口定义的lifecycleEvent方法 提供了自己的实现。
public void lifecycleEvent(LifecycleEvent event) { // Identify the host we are associated with try { host = (Host) event.getLifecycle(); if (host instanceof StandardHost) { setCopyXML(((StandardHost) host).isCopyXML()); setDeployXML(((StandardHost) host).isDeployXML()); setUnpackWARs(((StandardHost) host).isUnpackWARs()); setContextClass(((StandardHost) host).getContextClass()); } } catch (ClassCastException e) { log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e); return; } // 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(); } }
start方法会调用deployApps 方法。deployApps方法如下:
protected void deployApps() { File appBase = appBase();// 在server.xml中的Host标签指定appbase的属性为 webapps File configBase = configBase(); String[] filteredAppPaths = filterAppPaths(appBase.list());//列出appBase下的所有文件、文件夹,进行过滤 // Deploy XML descriptors from configBase deployDescriptors(configBase, configBase.list()); // Deploy WARs deployWARs(appBase, filteredAppPaths);//部署war包 // Deploy expanded folders deployDirectories(appBase, filteredAppPaths);//部署项目文件夹 }deployDirestories代码如下:
protected void deployDirectories(File appBase, String[] files) { if (files == null) return; ExecutorService es = host.getStartStopExecutor(); List<Future<?>> results = new ArrayList<Future<?>>(); 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); } } }
DeployDirectory代码如下:
private static class DeployDirectory implements Runnable { private HostConfig config; private ContextName cn; private File dir; public DeployDirectory(HostConfig config, ContextName cn, File dir) { this.config = config; this.cn = cn; this.dir = dir; } @Override public void run() { config.deployDirectory(cn, dir); } }
Hostconfig.deployDirectory代码如下:
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; File xml = new File(dir, Constants.ApplicationContextXml); File xmlCopy = new File(configBase(), cn.getBaseName() + ".xml"); DeployedApplication deployedApp; boolean copyThisXml = copyXML; try { if (deployXML && 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(); } } } if (copyThisXml == false && context instanceof StandardContext) { // Host is using default value. Context may override it. copyThisXml = ((StandardContext) context).getCopyXML(); } if (copyThisXml) { InputStream is = null; OutputStream os = null; try { is = new FileInputStream(xml); os = new FileOutputStream(xmlCopy); IOTools.flow(is, os); // Don't catch IOE - let the outer try/catch handle it } finally { try { if (is != null) is.close(); } catch (IOException e){ // Ignore } try { if (os != null) os.close(); } catch (IOException e){ // Ignore } } context.setConfigFile(xmlCopy.toURI().toURL()); } else { context.setConfigFile(xml.toURI().toURL()); } } else if (!deployXML && xml.exists()) { // 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).newInstance();//创建StandardContext的实例 } Class<?> clazz = Class.forName(host.getConfigClass()); LifecycleListener listener = (LifecycleListener) clazz.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() && deployXML && 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 (deployXML && 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))); } }
通过上面的代码可以确认,Host添加child 会把webapps文件夹下的项目添加进去,并且直接调用child的start方法。context 添加了一个listener,该listener 是类
org.apache.catalina.startup.ContextConfig 的实例,在StandardHost中有定义:private String configClass ="org.apache.catalina.startup.ContextConfig";
3、org.apache.catalina.startup.ContextConfig
public void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with try { context = (Context) event.getLifecycle(); } catch (ClassCastException e) { log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e); return; } // Process the event that has occurred if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { configureStart(); } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { beforeStart(); } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { // Restore docBase for management tools if (originalDocBase != null) { context.setDocBase(originalDocBase); } } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) { configureStop(); } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) { init(); } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) { destroy(); } }
结论: 将web项目部署到tomcat之后
tomcat启动:Catalina.start-->Server.start-->Service.start-->StandardEngine.start-->StandardHost.start
---》根据server.xml配置的Host的appbase 确认 存放web项目的位置,也就是 Tomcat根路径下的webapps文件夹
--》扫描webapps文件夹下的所有文件/文件夹,根据文件/文件夹的名字命名context,context默认是StandardContext的实例
--》将生成的context通过调用Host的addChild方法 添加到Host中,这里的Host 是StandardHost的实例
--》addChild 的时候 会自动调用child 的start方法。 这里会调用StandardContext的start方法。
至于对StandardContext的分析,放在后面进行。
- Tomcat 源码阅读(七)Tomcat加载web项目
- tomcat源码阅读步骤七
- Tomcat源码阅读系列(七)Session管理机制
- Tomcat源码阅读系列(六)类加载器
- 【Web容器】Tomcat源码分析(1)-类加载体系
- tomcat源码阅读-1
- tomcat源码阅读-2
- tomcat源码阅读-3.1
- Tomcat源码阅读计划
- Tomcat源码阅读
- Tomcat源码阅读系列
- Tomcat源码阅读1
- Tomcat源码阅读二
- tomcat加载web项目的大致步骤
- tomcat启动加载web项目内存溢出
- tomcat启动加载web项目内存溢出
- Tomcat源码阅读(一)初始化
- Tomcat源码阅读(二)Bootstrap
- BZOJ 2212 [Poi 2011] 线段树合并 解题报告
- tittle标题图标
- [spring学习] 之 spring-boot 默认配置
- Spring Boot -- Spring Data Jpa(6)
- android 拨打电话
- Tomcat 源码阅读(七)Tomcat加载web项目
- MAC下更新自带的PHP版本到5.6或7.0
- 渗透日记5:某站登入框存在post注入
- Maven依赖分析
- C++面向对象模型
- 点击图片调用相册或相机
- db2 工具直接解锁表
- 设计模式-原型模式
- android上实现二维码生成和扫描