Tomcat 源码阅读(七)Tomcat加载web项目

来源:互联网 发布:电子竞技数据分析 编辑:程序博客网 时间:2024/05/23 20:07
环境:tomcat7.0.28

坚持一下,把源码看完,勤奋一点,不要在懒惰了,你已经落下别人很多了


本文主要介绍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的分析,放在后面进行。









原创粉丝点击