Tomcat 源码阅读(二)Catalina.load

环境:tomcat7.0.28

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

本文主要讲解Catalina的load方法,顺带简单介绍一下Digester组件

一、总体流程


二、代码解析

1、createStartDigester()方法解析

 	long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        HashMap<Class<?>, List<String>> fakeAttributes =
            new HashMap<Class<?>, List<String>>();
        ArrayList<String> attrs = new ArrayList<String>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);

        // Configure the actions we will be using
        //创建StandardServer,当遇到Server标签的时候
        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
        digester.addSetProperties("Server");//为StandardServer属性设置值
        //在Catalina.load方法中,当定义好所有规则之后,会把Catalina对象压栈,此时去解析server.xml,会将Server对象压栈,然后调用Catalina的setServer方法
        digester.addSetNext("Server","setServer","org.apache.catalina.Server");
        //创建NamingResources遇到server标签下的GlobalNamingResources标签的时候
        digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");//设置属性
        digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
        //在配置文件里 Listener 标签有个className 的属性,根据读入的值进行类的创建
        digester.addObjectCreate("Server/Listener", null,  "className");//第二个参数为空,说明需要在server.xml中Listener标签指定实现类
        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,  "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, "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/");

        long t2=System.currentTimeMillis();
        if (log.isDebugEnabled()) {
            log.debug("Digester for server.xml created " + ( t2-t1 ));
        }
        return (digester);

通过查看代码可以知道,基本上的操作都是在调用digester的方法。

1.1Digester简介

  Digester是 apache的一个组件,通过它可以很方便的从xml文件生成java对象。

Digester由"事件"驱动,通过调用预定义的规则操作对象栈,将XML文件转换为Java对象。工作原理如下:
Digester底层采用 SAX解析XML文件,所以很自然的,对象转换由"事件"驱动,即在识别出特定XML元素时(实际被细分为begin、body、end、finish四个时点),将执行特定的动作,比如创建特定的Java对象,或调用特定对象的方法等。此处的XML元素根据匹配模式(matching pattern)识别,而相关操作由规则(rule)定义。在转换过程中,Digester维持了一个对象栈,可以看作对象转换的工作台,用来存放转换中生成的、或是为转换临时创建的Java对象。对输入XML文件作了一趟完整的扫描后,对象栈的栈顶元素即为目标对象。由于Digester屏蔽了 SAX解析的细节,使用者仅需关注转换操作本身,大大简化了转换操作。关于Digester的简单实用,读者可以参考这篇文章: 点击打开链接

1.2  Digester.addObjectCreate 方法解析

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

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

    }

调用addRule方法,同时封装一个ObjectCreateRule 的规则。

1.3Digester.addRule() 方法解析

public void addRule(String pattern, Rule rule) {

        rule.setDigester(this); //将规则rule 与 Digester进行绑定
        getRules().add(pattern, rule);

    }

1.4 Digester.getRules() 方法解析

 public Rules getRules() {

        if (this.rules == null) {
            this.rules = new RulesBase();
            this.rules.setDigester(this);
        }
        return (this.rules);

    }
走到这里的时候,我们可以看到在Digester中声明了一个Rules的对象rules。

1.5 Rules 和RulesBase

Rules类是一个接口类,我们可以把它看做是操纵 一组规则(Rule)的工具类。既然是操作一组规则,那么它的实现类肯定需要定义一个集合存储这组规则。
RulesBase 是Rules 的实现类,并且它的数据结构如下:
  /**
     * The set of registered Rule instances, keyed by the matching pattern.
     * Each value is a List containing the Rules for that pattern, in the
     * order that they were originally registered.
     */
    protected HashMap<String,List<Rule>> cache =  new HashMap<String,List<Rule>>(); //Rule集合的键值对


    /**
     * The Digester instance with which this Rules instance is associated.
     */
    protected Digester digester = null;   //关联的解析器


    /**
     * The namespace URI for which subsequently added <code>Rule</code>
     * objects are relevant, or <code>null</code> for matching independent
     * of namespaces.
     */
    protected String namespaceURI = null;  //命名空间URI,主要终于查找


    /**
     * The set of registered Rule instances, in the order that they were
     * originally registered.
     */
    protected ArrayList<Rule> rules = new ArrayList<Rule>();    //所有的Rule的集合

前文中  我们可以看到Digester的addRule方法,调用了 Rules定义的add方法,我们就直接查看RulesBase的add方法的代码:
public void add(String pattern, Rule rule) {
        // to help users who accidently add '/' to the end of their patterns
        int patternLength = pattern.length();
        if (patternLength>1 && pattern.endsWith("/")) {
            pattern = pattern.substring(0, patternLength-1);
        }
        
        
        List<Rule> list = cache.get(pattern);
        if (list == null) {
            list = new ArrayList<Rule>();
            cache.put(pattern, list);
        }
        list.add(rule);
        rules.add(rule);
        if (this.digester != null) {
            rule.setDigester(this.digester);
        }
        if (this.namespaceURI != null) {
            rule.setNamespaceURI(this.namespaceURI);
        }


    }
 通过上面的代码不难发现,该add方法主要是往cache 键值对里面添加rule。

了解Digester,还得需要知道server.xml的大体结构,读者可以自行去查看tomcat/conf/server.xml的内容。

结论1:通过以上代码 我们就可以知道 创建Digester对象的时候,会定义各种各样的规则,在上面的方法中,我们可以看到总共调用了以下这么几个规则:

(1)ObjectCreateRule   对象创建规则,并且在Digester中入栈,当遇到元素的末尾,则该对象出栈<
(2)SetPropertiesRule  栈顶对象元素设置规则,获取但不取出栈顶对象,针对该对象,如果xml文件配置了相应的属性,则调用对象中对应属性的set方法设置值。
(3)SetNextRule             设置栈顶第二个元素(父)与栈顶第一个元素(子)之间的父子关系规则。
(4)ConnectorCreateRule  连接器创建规则
(5)SetAllPropertiesRule    通过自省和反射机制设置属性的规则
以及一些嵌套元素的规则集合RuleSet,RuleSet可以看做一组Rule集合的封装,RuleSet定义了一个addRuleInstances方法,其实是为Digester定义一系列的规则,就想批处理一样。将一堆命令封装起来,外面只需要调用批处理即可,不用再手动写很多命令。放在这里也一样,可以直接调用封装的RuleSet,使代码更加的简洁。
(1)NamingRuleSet  处理JDN 资源池的 规则集合
(2)EngineRuleSet    处理Engine元素定义的内容 的规则集合
(3)HostRuleSet        处理Host元素定义内容的规则集合
(4)ContextRuleSet   处理Context元素定义内容的规则集合

结论2:Digester定义了很多规则,在解析之前,Digester首先把Catalina对象入栈,通过查看createStartDigester方法,可以确认,通过解析server.xml, Digester
(1) 创建了一个StandardServer对象,并且 调用了Catalina的setServer方法,
(2)创建了一个NamingResources对象,并且调用了StandardServer的setGlobalNamingResources方法
(3)创建了一个或多个LifecycleListener接口的子对象,该子对象在server.xml必须指定class属性,即实现类,并且调用了StandardServer的addLifecycleListener方法。 其实StandardServer的父类LifecycleBase 中有个LifecycleSupport类的对象,该对象有个LifecycleListener listeners[] 的数组。最终是数组成员添加。
(4)创建了一个或多个StandardService 对象,并且调用了StandardServer的addService方法。
(5)创建了一个或多个LifecycleListener接口的子对象,不过是嵌套在标签<Service> </Service> 标签内的listener,
(6)创建了一个或多个Executor对象,并且调用了StandardService的addExecutor方法。
(7)创建了一个或多个Connector对象,并且调用了StandardService的addConnector方法。
(8)创建了一个StandardEngine对象,并且调用了StandardService 的setContainer 方法
(9)创建了一个或多个StandardHost对象,并且调用了StandardEngine的addChild方法。
(10)创建了一个或多个StandardContext对象,并且调用了StandardHost的addChild方法。
再上一张图:

就上面这张图而言,罗列了大致的Tomcat的接口 ,实现类,抽象类之间的关系 以及 各个类之间的包含关系,1:1,1:n分别表示1对1 和1对多的关系。

2、Catalina.getServer().init();方法解析

通过使用digester解析server.xml,我们可以确定Catalina的server对象是类StandardServer的对象。
StandardServer类的继承关系如下

调用StandardServer的init方法其实是调用LifecycleBase的init方法,该方法代码如下:
 public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        try {
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            initInternal();
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }
    }

最终会调用子类的initInternal方法。注意 设置state 的时候方法是用synchronized修饰的。

3、StandardServer.initInternal()方法解析:


 protected void initInternal() throws LifecycleException {
        
        super.initInternal();

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");

        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");
        
        // Register the naming resources
        globalNamingResources.init();
        
        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

在代码最后一行,启动所有service的初始化方法init, 该services 是在Digester解析server.xml的时候  注入到Server里的。

4、StandardServer.initInternal()方法解析

 protected void initInternal() throws LifecycleException {

        super.initInternal();
        
        if (container != null) {
            container.init();
        }

        // Initialize any Executors
        for (Executor executor : findExecutors()) {
            if (executor instanceof LifecycleMBeanBase) {
                ((LifecycleMBeanBase) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize our defined Connectors
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed", connector);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }
(1)在 service 的initInternal中,首先 判断容器 container 是否进行了初始化。我们在Catalina的createStartDigester 方法中会发现有这么一行代码
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
继续深入查看EngineRuleSet 的addRuleInstances代码可以发现有这么一条规则:
 digester.addSetNext(prefix + "Engine","setContainer",  "org.apache.catalina.Container");
也就是说在Digester解析server.xml的时候,如果在<service></service>标签中发现了<Engine></Engine>的元素,这时候会先创建Engine的对象也就是StandardEngine,然后通过调用StandardService中的setContainer 方法 将StandardEngine 注入到StandardService中。

(2)如果在<Service></Service>定义了线程池,则调用线程池的init方法。

线程池的实现类,首先查看Catalina的createStartDigester方法:

 	//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");
从上面就可以确定Executor的实现类是StandardThreadExecutor。在该类中,没有进行额外的初始化工作。


(3)最后对连接器Connector进行初始化。连接器在后面的章节进行分析。




猜你喜欢

转载自blog.csdn.net/zhaoxinglin123/article/details/77968720
今日推荐