zoukankan      html  css  js  c++  java
  • 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() {
        	//这里剔除了一些推断,日志,server钩子函数,等代码
        	......
            if (getServer() == null) {
                load();
            }
       		......
            getServer().start();
    
            if (await) {
                await();
                stop();
            }
        }

    依据第一个if语句能够知道server是通过load方法实例化的。load方法运行后,启动服务器,逻辑非常easy。进入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在被解析时会依据当前的事件(開始,或结束)来调用相应节点的解析规则,创建对象,并通过栈来完毕对象之间的关联关系。


  • 相关阅读:
    浏览器内置对象及其方法
    Leetcode | Path Sum I && II
    Leetcode | 3Sum
    算法分析之渐近符号
    Leetcode | Two Sum
    Leetcode | Wildcard Matching
    LeetCode | Regular Expression Matching
    Leetcode | Subsets I & II
    Leetcode | Populating Next Right Pointers in Each Node I & II
    爱是恒久忍耐,又有恩慈
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4359707.html
Copyright © 2011-2022 走看看