tomcat源码Catalina

Catalina的作用是初始化各个组件,并开始启动各个组件。

上文中介绍了Bootstrap是如何启动Catalina的,现在来看看Catalina的作用:

1,Catalina通过Digester类加载server.xml,实例化server.xml中各个组件,并为这些实例赋值(这个类是通过扩展SAX来完成的)。

2,调用server的start方法开启server组件,server会一级一级的将start传播下去,这样各个组件就从这里开启了。

3,初始化命名空间(tomcat会使用JNDI技术,比如在server.xml中配置了数据库连接池的话,就使用了JNDI)。最后还包装了System.out和System.err。

这里面的重点就是Digester解析server.xml的过程,先来看看start方法:

  public void start() {
    	//这里剔除了一些判断,日志,服务器钩子函数,等代码
    	......
        if (getServer() == null) {
            load();
        }
   		......
        getServer().start();

        if (await) {
            await();
            stop();
        }
    }

根据第一个if语句可以知道server是通过load方法实例化的。load方法执行后,启动服务器,逻辑很简单。进入load方法:

  public void load() {

    	//这个方法的篇幅过长,我踢掉一些对流程不重要的代码
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
        	//获取sever.xml配置文件
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("catalina.configFail", file), e);
            }
        }

        //将sever.xml对应的流传给digester,由digester解析它。
        inputSource.setByteStream(inputStream);
        //这个方法很重要,它将自己也就是Catalina这个对象放到了digester对象里的一个栈里面,后面解析xml实例化server后
        //会从栈里拿出Catalina对象调用它的setServer方法来设置Catalina.server属性。server里的service属性也是通过这种形式
        //设置的。
        digester.push(this);
        digester.parse(inputSource);

        //server实例化后,将设置server.catalina属性,这里Catalina和Server是双向关联的。
        getServer().setCatalina(this);

        // 包装了System.out和System.err
        initStreams();

        getServer().init();

    }

如果想搞清楚出digester是如何加载server.xml并实例化各个组件的,你可能会进入 digester.parse(inputSource)方法,但这个方法

不能告诉你关键的内容。来看看digester是个什么类:

public class Digester extends DefaultHandler2

DefaultHandler2是SAX扩展包下面的,不懂SAX的一定要先去了解下SAX,不然看Catalina的源码很费劲。回过头来看看load()方法中的

第一行代码Digester digester = createStartDigester();进入createStartDigester方法:

   /**
     * Create and configure the Digester we will be using for startup.
     */
    protected Digester createStartDigester() {

        Digester digester = new Digester();

        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResources");

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");

        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        //Executor
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor",
                            "addExecutor",
                            "org.apache.catalina.Executor");

        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector",
                         new SetAllPropertiesRule(new String[]{"executor"}));
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");

        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        // When the 'engine' is found, set the parentClassLoader.
        digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(parentClassLoader));
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

        return (digester);

    }

仔细看着这些参数,xml文档的节点名称都有与之对应的类比如Server节点对象org.apache.catalina.core.StandardServer

digester实例化Server时是实例化了它的子类StanderServer。

进入addObjectCreate方法:

public void addObjectCreate(String pattern, String className,
                                String attributeName) {

        addRule(pattern,
                new ObjectCreateRule(className, attributeName));

    }

每调用一个add*方法都为pattern映射了一个Rule的子类,addSetProperties,addSetNext都为pattern映射了一个Rule的子类。

这个映射关系被存放在RulesBase.cache中:

public class RulesBase implements Rules {

    protected HashMap<String,List<Rule>> cache =
        new HashMap<String,List<Rule>>();
}

从这个数据结构可以知道每个节点的完整名称对应一个Rule的list集合。来看看Rule是如何定义的:

public abstract class Rule {
    public Digester getDigester() {
        return (this.digester);
    }
    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
        begin(attributes);
    }
    public void end(String namespace, String name)
        throws Exception {
        end();
    }
}

begin和end是它的核心方法(有的子类可能没有其中的某个方法,这里为了说明xml的解析流程所以说它们重要)。说到这里是想说明:digster事先为xml中的每个节点定义了多个规则。上面提到Degister继承自DefaultHandler2,并重写了它的startDocument(),startElement(),endDocument()endElement()等方法。Degister内部定义了一个XMLReader类型的成员变量reader,并将自己作为ContentHandler和DTDHandler
传给自己的成员变量reader。那么reader在解析xml的时候就会回调Digster继承自DefaultHandler2的startDocument(),startElement(),endDocument(),endElement()等回调方法。在回调方法中就会调用与当前节点对应的Rule类的回调方法。现在拿Server的初始化来举例:在上面提到的createStartDigester()方法中为Server添加了三个规则:

   digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

这三个方法内部会分别创建三个规则类,ObjectCreateRule,SetPropertiesRule,SetNextRule。这三个类的作用分别是创建指定类,将xml标签上的属性赋值给改类,将该类赋值给它的上级类。这三个类被创建后添加到了一个以“Server”为键的map中前面提到的RulesBase.cache。在reader类解析xml到<Server>标签的开始节点时会调用startElement()方法,并将当前节点的节点名和属性值等一系列值传给改方法。方法内部则通过节点全名称获取对应的规则类Rule对象的list结合,并挨个调用rule对象的begin方法。以server对应的ObjectCreateRule规则为例:

    public void begin(String namespace, String name, Attributes attributes)
            throws Exception {
        Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
        Object instance = clazz.newInstance();
        digester.push(instance);
    }

创建了与server对应的实例(实例名是org.apache.catalina.core.StandardServer或是属性classname对象的值)并放在了digest的一个名为stack属性的栈顶。前面的load方法中digester将当前类对象也就是Catalina对象push到了stack里面,这时候Catalina应该在stack的底端,因为之前stack里没有数据。server标签是server.xml的第一个标签,这时候解析到它的开始标签并调用了与它对象的ObjectCreateRule规则的begin方法初始化了server对象并后push到了stack里面,那么此时stack有两个元素,Catalina在栈底,server在栈顶。ObjectCreateRule的start方法结束后会继续调用SetPropertiesRule的start方法,这个类是将标签的属性值赋值给上面创建的对象,它的begin方法的第一步就是从stack获取对象,然后将标签上的属性值赋值给该对象比如<Server
port="8005" shutdown="SHUTDOWN">中的port和shutdown属性。最后是SetNextRule类,这个类只有一个end方法是在遇到</Server>的结束标签时调用的。下面是该方法的源码:

    @Override
    public void end(String namespace, String name) throws Exception {

        Object child = digester.peek(0);
        Object parent = digester.peek(1);
        if (digester.log.isDebugEnabled()) {
            if (parent == null) {
                digester.log.debug("[SetNextRule]{" + digester.match +
                        "} Call [NULL PARENT]." +
                        methodName + "(" + child + ")");
            } else {
                digester.log.debug("[SetNextRule]{" + digester.match +
                        "} Call " + parent.getClass().getName() + "." +
                        methodName + "(" + child + ")");
            }
        }

        IntrospectionUtils.callMethod1(parent, methodName,
                child, paramType, digester.getClassLoader());

    }

在调用这个方法前,degister的成员变量stack已经push和pop好几个对象了,每次标签开始解析时创建对象push到stack里面,标签结束后从stack里pop出来。因为<Server>标签是顶层标签,所以server对象最先被创建并push到stack里面(Catalina一直在栈底),最后被pop出来,所以结束标签</server>解析时和开始一样,stack依然还是两个元素,server在栈顶,Catalina在栈底。 Object child = digester.peek(0);Object
parent = digester.peek(1);分别取出了栈顶和栈底的元素。server.xml的解析不是很好叙述,它的关系有点复杂。要记住一点server.xml的每一级标签对应一个tomcat组件,被外层标签包裹的标签是外层标签的一个属性比如serveice是server的一个属性。xml在被解析时会根据当前的事件(开始,或结束)来调用对应节点的解析规则,创建对象,并通过栈来完成对象之间的关联关系。

时间: 2024-08-09 05:30:10

tomcat源码Catalina的相关文章

Tomcat源码分析之—具体启动流程分析

从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息.Catalina.base信息,在initClassLoaders方法中初始化类加载器,然后通过反射初始化org.apache.catalina.startup.Catalina作为catalina守护进程: 一.load Bootstrap中load流程: 反射调用Catalina的load方法

TOMCAT源码分析(启动框架)

建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程. 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助. 我相信这样效果以及学习速度都会好很多! 1. Tomcat的整体框架结构 Tomcat的基本框架, 分为4个层次. Top Level Elements: Server Service Connector HTTP AJP Conta

Tomcat 源码分析(转)

Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件,连接器和容器,所谓连接器就是一个http请求过来了,连接器负责接收这个请求,然后转发给容器.容器即servlet容器,容器有很多层,分别是Engine,     Host,Context,Wrapper.最大的容器Engine,代表一个servlet引擎,接下来是Host,代表一个虚拟机,然后是Context,代表一个应用,Wrapper对应一个servlet.从连接器     传过来连接后,容器便会顺序经过上面的容器,最

tomcat源码学习(2)&#160;&#160;关于apache&#160;digest

好久不写博文,罪过罪过.因为最近公司比较忙加上琐事有点多,所以隔了好久才来更新博文. apache digest本来是struts2框架中来加载xml文件并实例化对象的一个jar包,后来使用的越来越多. 我们都知道tomcat的conf文件夹下有一个server.xml配置文件,我们经常会其中的来进行配置以来运行一个java web项目,也经常修改中的port属性以来实现修改tomcat监听的端口.其实每个标签基本上都对应着一个对象,那tomcat是如何将这些对象实例化到java 虚拟机的运行内

tomcat源码分析前的准备工作

Tomcat源码学习前的准备工作 注:由于网上的帖子大部分没有配套的图片和错误的分析,所有费了半天劲整理了此篇博客,希望大家少走弯路吧 下面我们就开始我们的Tomcat源码学习之旅. 1. 下载Tomcat6.0的源代码 首先,我们得下载Tomcat6.0的源代码.Tomcat源代码的版本控制工具不是CVS,而是Subversion,如果您的机器上没有安装Subversion,请从http://subversion.tigris.org/servlets/ProjectDocumentList?

75篇关于Tomcat源码和机制的文章

75篇关于Tomcat源码和机制的文章 标签: tomcat源码机制 2016-12-30 16:00 10083人阅读 评论(1) 收藏 举报  分类: tomcat内核(82)  版权声明:本文为博主原创文章,未经博主允许不得转载. 整理下前面写过的75篇关于Tomcat源码和机制的文章 文章列表 如何设计一个Web容器 Web安全认证机制知多少 Tomcat集群实现源码级别剖析 Tomcat集群如何同步会话 从单机到集群会话的管理之集群模式一 从单机到集群会话的管理之集群模式二(更大的集群

tomcat源码分析(一)从tomcat架构说起

p { margin-bottom: 0.25cm; line-height: 120% } 首先dowload源码并导入到Eclipse中,导入后代码代码的层次如下图所示.先简单来看下tomcat源码的结构.javax这个包主要是有关JavaEE规范的,比如Servlet等等,并不是我们主要分析的.需要分析有org.apache下的子包,其中catalina中很重要,其中tomcat的启动就在该包下中的startup包下, catalina下还定义了tomcat容器(server,servic

tomcat源码导入eclipse

1. 获取源代码 方式一:从官网http://tomcat.apache.org/download-70.cgi 直接下载,官网提供了Binary 和 Source Code两种下载方式,要研究tomcat源代码,选择最下面的source code 方式下载 方式二:利用svn获取源代码,先安装svn,再新建目录tomcat7, 接着在目录里用 svn执行以下命令 svn co http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_

Tomcat源码分析——请求原理分析(下)

前言 本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过<TOMCAT源码分析——请求原理分析(上)>和<TOMCAT源码分析——请求原理分析(中)>.在<TOMCAT源码分析——请求原理分析(中)>一文我简单讲到了Pipeline,但并未完全展开,本文将从Pipeline开始讲解请求原理的剩余内容. 管道 在Tomcat中管道Pipeline是一个接口,定义了使得一组阀门Valve按照顺序执行的规范,Pipeline中定义的接口如下: getBas