zoukankan      html  css  js  c++  java
  • Tomcat8源码笔记(三)Catalina加载过程

    之前介绍过 Catalina加载过程是Bootstrap的load调用的  Tomcat8源码笔记(二)Bootstrap启动

    按照Catalina的load过程,大致如下: 接下来一步步分析加载过程

    image

    一.initDirs

    从系统环境变量、VM参数中读取java.io.tmpdir, 并校验文件夹合法性; 未指定java.io.tmpdir,会抛出异常,所以我们启动时指定VM参数:image

    -Djava.io.tmpdir=E:/Tomcat_Source_Code/apache-tomcat-8.0.53-src/catalina-home/temp

    二.initNaming

    initNaming设置一些额外的环境变量,在创建Digester时可能会用到.

    image

    三.createStartDigester

    createStartDigester作用: 实例化Digester,设置validating标志位false,rulesValidating标志位true,fakeAttributes为{Object.class=[className]},useContextClassLoader标志位true,以上几种属性标志位是为了进行解析server.xml而设置的 。  再接着添加了一大段的 addObjectCreate 、addSetProperties、addSetNext, 这些是用来设置解析server.xml的规则,Tomcat的server.xml我们没见过xsd、DTD来约束文档书写规则吧,但是也不能不按照顺序随意些,比如我们不能将Servers标签写到Service标签里面吧。下面会具体分析添加规则部分。

    image

    贴上一份Tomcat8中默认的Server.xml内容,虽然平时部署到Tomcat也没怎么仔细关注过它,可能关注也只是关注到Tomcat端口而已、或者简单配置虚拟图片服务器。现在仔细看这份xml文档,与常见的spring、mybatis的文档不同,前面没有声明DTD或XSD,所以Tomcat要自己定义规则、自己按照规则来解析这样一份XML。

    image

    四.Rule抽象类

    Rule抽象类的定义如下,省略了setter/getter方法,可以看出来Rule两个属性:关联的Digester以及命名空间namespaceURI,此外的方法就是begin、body、end、finish,这就是一个Rule规则实例化的生命周期,就是按顺序解析XML,按顺序调用begin、body、end、finish方法就能实例化对象;image

    Rule具体的实现类下面分析,代码中Digester又是addObjectCreate 、addSetProperties、addSetNext一大串,先来addObjectCreate:

    添加ObjectCreate时候,会先将Rule和Digester关联,然后每个规则加入到Digester的rules属性中,rules属性为Rules接口,默认采用RuleBase作为实现类。image

    Rules和RuleBase

    image

    RuleBase的add(String,Rule)方法:

    rules属性Rule的集合,目的是有序地保存最开始注册的Rule规则;cache属性,按照pattern来保存,目的是比如Server组件,创建ObjectCreate、属性赋值setPropertiesRule、设置调用方法SetNextRule三个属性规则,通过pattern 字符串就能将同一组件的不同规则保存在一起.  而pattern中 / 用来分隔标签下的子标签,比如Server下的Service,Server/Service.

     image

    五.configFile

    configFile默认为conf/server.xml,Bootstrap的catalinaBase之前Bootstrao启动时候静态代码块赋的值,从系统变量或VM参数中读取catalina.base;简而言之,就是读取catalina.base/conf/server.xml文件.

    image

    六.解析server.xml

    inputStream就是上面server.xml的FileInputStream ,赋给InputSource实例.  push以及parse方法下面记录.

    image

    六.一  Digester的push方法

    Digester实例将Catalina实例存入stack中,并且Digester的root属性也为Catalina实例,这里的ArrayStack是Tomcat自己实现的类,继承自ArrayList。 这一步将Catalina放入Digester的stack集合相当于第一位置,下标为0的位置,很关键!

    image

    六.二    Digester的parse方法, 解析server.xml的地方

    configure是初始化打印日志log ; getXmlReader深入发现就是调用SAXParserFactory.newInstance().newSAXParser(),采用SAX的方式解析server.xml,并且设置内容解析器为this对象,也就是Digester,Digester重写了解析的规则。

    image

    很想贴一张SAXParser解析流程图,找了半天没搜到,我简单介绍下,SAX解析过程中DefaultHandler类,Digester就继承了DefaultHandler2,当然也继承了DefaultHandler。这个接口呢,startDocument开始解析xml时执行,startElement开始解析元素时执行,characters解析元素内容,endElement结束元素解析,然后循环往复解析,直到解析完成之后endDocument . SAX解析xml优点内存占用小,解析速度快,因为一边读取xml一边解析。Digester类的startDocument就没必要看了,直接从startElement开始记录.

     

    startElement解析过程

    server.xml中元素namespaceURI都为空,match解析到Server就是Server,解析到Server下Service就是Server/Service,与之间加入到Digester的pattern是一致的,这里就按照这个规则找到Rule的集合,加入到matches中,并且遍历Rule集合分别调用begin方法,比如以Server元素为例。

    image

    Server标签解析:

    Server一共有三个Rule,分别是ObjectCreateRule、SetPropertiesRule、SetNextRule

    此时ObjectCreateRule的className为org.apache.catalina.core.StandardServer,attributeName为className。 尝试从xml解析的attributes中找className,没有的情况下就用前面的className,使用当前线程的类加载器加载StandardServer,并且使用空参构造器实例化一个StandardServer,具体的StandardServer实例化下章记录,实例化完成的server加入到digester的stack,就是前面说的那个ArrayList子类中,第一位是Catalina,所以StandardServer是第二位.

    image

    SetPropertiesRule

    digester peek方法返回栈首,深入查看发现就是其stack中最后一位元素,即上面的StandardServer;之后遍历attributes,可以理解为键值对集合,如果键是”className“,isFakeAttribute返回true,后面设置属性的方法就不会执行;如果键不是“className“,就会给StandardServer设置属性,IntrospectionUtils.setProperty可以理解为给对象属性赋值的方法。  比如常见的 <Server port="8005" shutdown="SHUTDOWN">  ,这个xml解析之后attributes就是{port=8005,shutdown=SHUTDOWN},给对象属性赋值方法,也就是遍历对象的方法尝试寻找setPort/setShutdown,然后反射调用。

    image

    SetNextRule的begin方法没有做任何处理,跳过;另外server.xml中元素都没有body内容,characters我们也跳过;下面SAX解析发现又会跳进入到startElement中,因为Server元素没有结束,还有子元素要解析。

    image

    下面继续以一个Listener元素进行分析,仍然是startElement,之前match=”Server”,还没结束,现在match变成了“Server/Listener”

            StringBuilder sb = new StringBuilder(match);
            if (match.length() > 0) {
                sb.append('/');
            }
            sb.append(name);
            match = sb.toString();
            if (debug) {
                log.debug("  New match='" + match + "'");
            }
    
            // Fire "begin" events for all relevant rules
            List<Rule> rules = getRules().match(namespaceURI, match);
            matches.push(rules);

    之前向Digester添加解析规则时,添加了这样的规则:

    image

    所以现在通过Server/Listener同样可以得到三个Rule,和Server一样解析,调用空参构造器初始化第一个VersionLoggerListener,设置属性(虽然没有属性值要设置,因为className不在设置属性范围内),setNextRule(虽然这个begin什么也没做), 可以进入endElement分析,第一个endElement是Listener元素的:

    endElement伪代码如下: matches也是ArrayStack类型的,pop返回ArrayList集合的最后一个元素,也就是Listener的List<Rule>,遍历Rule集合,分别调用其body方法;因为Tomcat的server.xml大部分或者几乎就没有元素体有内容的元素,所以Rule实现类的body方法都是空实现;  逆序遍历Rule集合,分别调用Rule的end方法 ; 最后结束一个元素,就将match的 / 去掉一重,保证下次解析正确性;

    image

    SetNextRule的end方法记录:

    peek(0)返回digester中最后添加的一个元素,peek(1)返回倒数第二个添加的元素,分别是VersionLoggerListener以及StandardServer,反射调用peek(1)也就是StandardServer的addLifecycleListener,将peek(0)添加到StandardServer的监听器集合。 

    image

    ObjectCreateRule的end方法

    将当前正在创建的元素从digester中移除,ObjectCreateRule所以是最后执行的,start的时候顺序调用Rule集合,end时候逆序调用Rule集合;

    image

    上述就是Server元素所有子元素的解析流程,循环往复,直到最后才解析Server元素的endDocument方法,而前面分析得到Rule集合的end方法,只需要关注SetNextRule和ObjectCreateRule的end方法即可。到了Server时候Digester的stack中元素如下,[Catalina,StandardServer],所以按照分析会调用Catalina的setServer将解析完成的StandardServer赋给Catalina ; 而StandardServer的ObjectCreateRule会将 StandardServer从Digester移除; 加载过程中另外解析的xml,因为暂时不知道用途,这里就不记录了。

     

    七. Catalina加载过程后续步骤

    之前将StandardServer赋给了Catalina,现在也将Catalina赋给StandardServer,双向引用;之后给StandardServer设置catalina.home和catalina.base;初始化流,主要是控制台输出流改变,再之后调用StandardServer的init方法初始化容器!StandardServer初始化工作比较复杂,留作下篇博客记录。

    image

    简单按照Tomcat的server.xml解析规则,自定义了一个测试类,完成解析XML,目的是查看解析的效果:对Tomcat各个组件有个直观的了解。

    import org.apache.catalina.*;
    import org.apache.catalina.Server;
    import org.apache.catalina.connector.Connector;
    import org.apache.catalina.core.StandardHost;
    import org.apache.catalina.deploy.NamingResourcesImpl;
    import org.apache.catalina.startup.*;
    import org.apache.tomcat.util.digester.ArrayStack;
    import org.apache.tomcat.util.digester.Digester;
    import org.apache.tomcat.util.digester.Rule;
    import org.xml.sax.Attributes;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.ext.DefaultHandler2;
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParserFactory;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    
    public class ParserTest {
        private String match="";
        private ArrayStack<List<Rule>> stack=new ArrayStack();
    
        public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
            ParserTest test = new ParserTest();
            test.test();
        }
    
        public void test() throws IOException, ParserConfigurationException, SAXException {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            InputSource inputSource = new InputSource();
            inputSource.setByteStream(new FileInputStream("E:\Tomcat_Source_Code\apache-tomcat-8.0.53-src\conf\server.xml"));
            Digester digester=new Digester();
            addRule(digester);
            digester.push(new Catalina());
            factory.newSAXParser().parse(inputSource,new DefaultHandler2(){
                @Override
                public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
                    String name = localName;
                    if ((name == null) || (name.length() < 1)) {
                        name = qName;
                    }
                    if (match.length()>0) match+="/";
                    match+=name;
                    List<Rule> rules = digester.getRules().match(uri, ParserTest.this.match);
                    stack.push(rules);
                    if(rules!=null && rules.size()>0){
                        for(int i=0;i<rules.size();i++){
                            Rule rule = rules.get(i);
                            try {
                                rule.begin(uri,name,attributes);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
    
                @Override
                public void endElement(final String uri, final String localName, final String qName) throws SAXException {
                    String name = localName;
                    if ((name == null) || (name.length() < 1)) {
                        name = qName;
                    }
                    List<Rule> rules = stack.pop();
                    if(rules!=null && rules.size()>0){
                        for(int i=0;i<rules.size();i++){
                            Rule rule = rules.get(i);
                            try {
                                rule.body(uri,name,"");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
    
                    if(rules!=null){
                        for(int j=rules.size()-1;j>=0;j--){
                            Rule rule = rules.get(j);
                            try {
                                rule.end(uri,name);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    if(match.lastIndexOf("/")>0){
                        match=match.substring(0,match.lastIndexOf("/"));
                    }else{
                        match="";
                    }
                }
    
                @Override
                public void characters(final char[] ch, final int start, final int length) throws SAXException {
                    super.characters(ch, start, length);
                }
    
                @Override
                public void endDocument() throws SAXException {
                    if(stack.size()>0){
                        stack.pop();
                    }
                    Iterator<Rule> iterator = digester.getRules().rules().iterator();
                    while(iterator.hasNext()){
                        Rule rule = iterator.next();
                        try {
                            rule.finish();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    match="";
                    stack.clear();
                }
            });
            System.out.println("解析XML完毕:");
            Catalina catalina = (Catalina) digester.getRoot();
            System.out.println("Cataline信息:"+catalina);
            Server server = catalina.getServer();
            System.out.println("Server信息:"+server);
            System.out.printf("Server端口:%s,SHUTDOWN:%s
    ",server.getPort(),server.getShutdown());
            NamingResourcesImpl globalNamingResources = server.getGlobalNamingResources();
            System.out.println("GlobalNamingResources信息:"+globalNamingResources);
            LifecycleListener[] listeners = server.findLifecycleListeners();
            for (LifecycleListener listener:listeners) {
                System.out.println("Listener信息:"+listener);
            }
            Service[] services = server.findServices();
            for (Service service:services) {
                System.out.println("Service信息:"+service.getName());
                Connector[] connectors = service.findConnectors();
                for(Connector connector:connectors){
                    System.out.println("Connector信息:"+connector.getProtocol());
                }
                Container container = service.getContainer();
                System.out.println("Container容器信息:"+container.getName());
                Container[] children = container.findChildren();
                int i=1;
                for(Container child:children){
                    System.out.println(container.getName()+"子容器"+(i++)+"信息:"+child.getName());
                    if(child instanceof StandardHost){
                        StandardHost host= (StandardHost) child;
                        System.out.printf("APP_BASE: %s, UnpackWARS: %s, Auto_Deploy:%s
    " ,host.getAppBase(),host.isUnpackWARs(),host.getAutoDeploy());
                        Valve[] valves = host.getPipeline().getValves();
                        for(Valve valve:valves){
                            System.out.println(valve.getClass());
                        }
                    }
                }
            }
        }
    
        public static void addRule(Digester digester){
            HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
            ArrayList<String> attrs = new ArrayList<>();
            attrs.add("className");
            fakeAttributes.put(Object.class, attrs);
            digester.setFakeAttributes(fakeAttributes);
            digester.setUseContextClassLoader(true);
    
            // Configure the actions we will be using
            digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");  //初始化的Server对象StandardServer
            digester.addSetProperties("Server");
            digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
    
            digester.addObjectCreate("Server/GlobalNamingResources",
                    "org.apache.catalina.deploy.NamingResourcesImpl");
            digester.addSetProperties("Server/GlobalNamingResources");
            digester.addSetNext("Server/GlobalNamingResources",
                    "setGlobalNamingResources",
                    "org.apache.catalina.deploy.NamingResourcesImpl");
    
            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/"));
            digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
    
            // When the 'engine' is found, set the parentClassLoader.
            digester.addRule("Server/Service/Engine",new SetParentClassLoaderRule(Thread.currentThread().getContextClassLoader()));
        }
    
        public static class SetParentClassLoaderRule extends Rule {
    
            public SetParentClassLoaderRule(ClassLoader parentClassLoader) {
    
                this.parentClassLoader = parentClassLoader;
    
            }
    
            ClassLoader parentClassLoader = null;
    
            @Override
            public void begin(String namespace, String name, Attributes attributes)
                    throws Exception {
    
                if (digester.getLogger().isDebugEnabled()) {
                    digester.getLogger().debug("Setting parent class loader");
                }
    
                Container top = (Container) digester.peek();
                top.setParentClassLoader(parentClassLoader);
    
            }
        }
    }
    

     

    查看解析效果: 也让我们大致对Tomca组件有个了解,Catalina持有Server,一个Server持有多个Service,Service组件持有多个Connector连接器,负责对外监听HTTP、AJP等等连接;Service组件只能有一个Container,通常是Engine,Engine容器中有Host容器,Host中就是Context容器;

    image

    九. 总结

    Catalina加载的过程解析catalina.home下conf/server.xml,并完成实例化工作,并且调用了StandardServer init方法,总体流程是这样,具体细节后面再记录。

     

     

  • 相关阅读:
    Oracle 推出 ODAC for Entity Framework 和 LINQ to Entities Beta版
    Entity Framework Feature CTP 5系列文章
    MonoDroid相关资源
    MSDN杂志上的Windows Phone相关文章
    微软学Android Market推出 Web Windows Phone Marketplace
    使用 Visual Studio Agent 2010 进行负载压力测试的安装指南
    MonoMac 1.0正式发布
    Shawn Wildermuth的《Architecting WP7 》系列文章
    使用.NET Mobile API即51Degrees.mobi检测UserAgent
    MongoDB 客户端 MongoVue
  • 原文地址:https://www.cnblogs.com/lvbinbin2yujie/p/10660244.html
Copyright © 2011-2022 走看看