Tomcat源码阅读之Server.xml文件的处理与Catalina启动流程

来源:互联网 发布:程序员工作简历 编辑:程序博客网 时间:2024/05/29 04:38

上一篇文章初步看了一下Tomcat启动的入口,而且粗略了解了Tomcat的classLoader的设计。。。。

其实对于启动过程,Bootstrap对象只能算是一个入口吧,例如它初始化了一些目录,创建了最顶层的3个classLoader,然后对于接下来的启动过程就交给了Catalina对象来搞了。。。

另外,在Bootstrap对象中,将当前的线程classLoader设置为了Catalina(也就是专属于Tomcat服务器的classLoader),然后调用了创建的Catalina对象的setParentClassLoader方法,将sharedLoader传了进去,这里也就是属于web应用程序那条线的classLoader,。。。。。把这部分记住就可以开始下面的内容了。。。。

接下来就可以开始说Catalina对象的工作了。。。简单概括一下它的工作:

(1)解析配置的xml文件

(2)根据配置文件创建对象,例如Server,service,connector,container啥的。。

(3)初始化上面创建的对象

(4)启动上面创建的对象。。。

这四部完成,整个tomcat服务器就算是启动了。。。。

其实这重要的事情就是对配置xml进行解析处理。。。。

这里先来看看Catalina的start方法吧:

//创建一个tomcatServer的实例    public void start() {        if (getServer() == null) {            load();   //加载server,这里其实主要是解析xml文件,读取里面的元素定义,用于生成server对象,并且初始化server对象        }        if (getServer() == null) {            log.fatal("Cannot start server. Server instance is not configured.");            return;        }        long t1 = System.nanoTime();        // Start the new server        try {            getServer().start();  //调用server的start,最终启动tomcat服务器,其实server要做的是启动里面的service        } catch (LifecycleException e) {            log.fatal(sm.getString("catalina.serverStartFail"), e);            try {                getServer().destroy();            } catch (LifecycleException e1) {                log.debug("destroy() failed for failed Server ", e1);            }            return;        }        long t2 = System.nanoTime();        if(log.isInfoEnabled()) {            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");        }        // Register shutdown hook        if (useShutdownHook) {            if (shutdownHook == null) {                shutdownHook = new CatalinaShutdownHook();            }            Runtime.getRuntime().addShutdownHook(shutdownHook);            // If JULI is being used, disable JULI's shutdown hook since            // shutdown hooks run in parallel and log messages may be lost            // if JULI's hook completes before the CatalinaShutdownHook()            LogManager logManager = LogManager.getLogManager();            if (logManager instanceof ClassLoaderLogManager) {                ((ClassLoaderLogManager) logManager).setUseShutdownHook(                        false);            }        }        if (await) {            await();            stop();        }    }

这里先是调用load方法,用于解析xml文件,创建相应的对象,然后在调用server的start方法来真正的启动tomcat服务器,。。那么这里就主要来看看这个load方法做了什么事情吧。。。。

public void load() {        long t1 = System.nanoTime();        initDirs();  //初始化一些目录参数        // Before digester - it may be needed        initNaming();        // Create and execute our Digester        Digester digester = createStartDigester();  //创建处理xml文件的对象,并会生成相应的处理规则,例如如何创建server对象        InputSource inputSource = null;        InputStream inputStream = null;        File file = null;        try {            file = configFile();   //获取server.xml            inputStream = new FileInputStream(file);   //读取server.xml            inputSource = new InputSource(file.toURI().toURL().toString());        } catch (Exception e) {            if (log.isDebugEnabled()) {                log.debug(sm.getString("catalina.configFail", file), e);            }        }        if (inputStream == null) {            try {                inputStream = getClass().getClassLoader()                    .getResourceAsStream(getConfigFile());                inputSource = new InputSource                    (getClass().getClassLoader()                     .getResource(getConfigFile()).toString());            } catch (Exception e) {                if (log.isDebugEnabled()) {                    log.debug(sm.getString("catalina.configFail",                            getConfigFile()), e);                }            }        }        // This should be included in catalina.jar        // Alternative: don't bother with xml, just create it manually.        if( inputStream==null ) {            try {                inputStream = getClass().getClassLoader()                        .getResourceAsStream("server-embed.xml");                inputSource = new InputSource                (getClass().getClassLoader()                        .getResource("server-embed.xml").toString());            } catch (Exception e) {                if (log.isDebugEnabled()) {                    log.debug(sm.getString("catalina.configFail",                            "server-embed.xml"), e);                }            }        }        if (inputStream == null || inputSource == null) {            if  (file == null) {                log.warn(sm.getString("catalina.configFail",                        getConfigFile() + "] or [server-embed.xml]"));            } else {                log.warn(sm.getString("catalina.configFail",                        file.getAbsolutePath()));                if (file.exists() && !file.canRead()) {                    log.warn("Permissions incorrect, read permission is not allowed on the file.");                }            }            return;        }        try {            inputSource.setByteStream(inputStream);            digester.push(this);   //这里先将当前对象放到Digester的栈底,但会server生成之后会调用当前对象的setServer方法来保存起来            digester.parse(inputSource);        } catch (SAXParseException spe) {            log.warn("Catalina.start using " + getConfigFile() + ": " +                    spe.getMessage());            return;        } catch (Exception e) {            log.warn("Catalina.start using " + getConfigFile() + ": " , e);            return;        } finally {            try {                inputStream.close();            } catch (IOException e) {                // Ignore            }        }        getServer().setCatalina(this);  //设置sever的catalina对象        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());  //设置server的目录        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());        // Stream redirection        initStreams();        // Start the new server        try {            getServer().init();   //初始化创建的server对象,其实这里主要是初始化service        } catch (LifecycleException e) {            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {                throw new java.lang.Error(e);            } else {                log.error("Catalina.start", e);            }        }        long t2 = System.nanoTime();        if(log.isInfoEnabled()) {            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");        }    }

代码虽然很多,但是这里只要关注最核心的代码就好了。。首先初始化了一些目录的参数。。。。然后创建了Digester对象,这个对象是用于解析配置文件的关键对象。。。接着获取server.xml文件的输入流,然后利用刚刚创建的 Digester对象进行解析。。。。。

那么这里关键就是要搞定 Digester 对象的运行原理了。。。。先来看看createStartDigester方法都干了什么事情吧。。。

protected Digester createStartDigester() {        long t1=System.currentTimeMillis();        // Initialize the digester        Digester digester = new Digester();   //这个对象用于解析处理xml文件        digester.setValidating(false);        digester.setRulesValidation(true);        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对象的规则        digester.addSetProperties("Server");  //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性        digester.addSetNext("Server",                            "setServer",                            "org.apache.catalina.Server");   //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法        digester.addObjectCreate("Server/GlobalNamingResources",                                 "org.apache.catalina.deploy.NamingResourcesImpl");  //创建全局名字资源        digester.addSetProperties("Server/GlobalNamingResources");        digester.addSetNext("Server/GlobalNamingResources",                            "setGlobalNamingResources",                            "org.apache.catalina.deploy.NamingResourcesImpl");   //其实这里实在server对象上面设置resource        digester.addObjectCreate("Server/Listener",                                 null, // MUST be specified in the element                                 "className");   //根据xml文件配置来创建listener        digester.addSetProperties("Server/Listener");  //设置listener对象配置的属性        digester.addSetNext("Server/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");  //这里其实是在server对象上面添加lifecyclelistener        digester.addObjectCreate("Server/Service",                                 "org.apache.catalina.core.StandardService",                                 "className");    //创建service对象        digester.addSetProperties("Server/Service");  //设置service的属性        digester.addSetNext("Server/Service",                            "addService",                            "org.apache.catalina.Service");   //在server上面添加调用addService方法        //为service创建listener,不一定有        digester.addObjectCreate("Server/Service/Listener",                                 null, // MUST be specified in the element                                 "className");    //根据配置文件参数来创建service的listener        digester.addSetProperties("Server/Service/Listener");        digester.addSetNext("Server/Service/Listener",                            "addLifecycleListener",                            "org.apache.catalina.LifecycleListener");        //Executor        //创建service的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");        //为servie添加connector规则        digester.addRule("Server/Service/Connector",                         new ConnectorCreateRule());    //这个规则会创建connector,而且如果有属性执行executor的话,还会设置connector的executor        digester.addRule("Server/Service/Connector",  //<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>                         new SetAllPropertiesRule(new String[]{"executor"}));   //这里会将所有的属性都设置,除了executor        digester.addSetNext("Server/Service/Connector",                            "addConnector",                            "org.apache.catalina.connector.Connector");  //在service上面添加这个connector        //这里是设置connector的listener        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/"));   //engine元素的定义的处理,这里主要是创建eingie        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));  //engine里面host的定义        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));  //一些context的配置处理        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));  //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");        long t2=System.currentTimeMillis();        if (log.isDebugEnabled()) {            log.debug("Digester for server.xml created " + ( t2-t1 ));        }                return (digester);    }

Tomcat对xml配置文件的处理感觉还挺麻烦的,相对于jetty来说,jetty是将整个配置文件看成了类似于一个脚本来解释处理,但是Tomcat则是为每一个xml元素设置相应的处理规则。。。这里就举其中一个规则来说吧:

digester.addObjectCreate("Server",                                 "org.apache.catalina.core.StandardServer",                                 "className");   //创建一个创建server对象的规则        digester.addSetProperties("Server");  //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性        digester.addSetNext("Server",                            "setServer",                            "org.apache.catalina.Server");   //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法

这个意思就是说遇到Server这个元素的的时候创建了3个规则,第一个规则是创建server对象的规则,这里

org.apache.catalina.core.StandardServer

参数表示要创建的server的类型,然后还有一个参数是”className“,待会在处理的时候,会看Server元素是否在属性域设置了className,如果有的话,那么将会使用指定的类型来创建Server对象,而不是上面那个默认的类型。。。

接着创建了一个规则,这个规则用于设置创建的Server对象的属性,也就是根据XML文件的Server元素后面的属性来相应的设置当前的Server对象的属性。。。。

然后还创建了一个SetNext规则,这个规则比较有意思。。。。它表示在整个Server这个XML元素处理完了之后要做的动作,调用setServer方法,那么调用的是谁的setServer方法呢。。?其实调用的当前这个Catalina对象的SetServer方法将已经创建的Server对象保存起来。。。

这里也就是简单的介绍了一下规则的作用,其实 Digester的工作原理还是挺简单的,这里就不细说了。。。可以去看一下代码,很简单就能够明白。。。。基本上XML文件解析处理要用到的所有对象都在digester包中。。。。。

这部分搞明白了。。。就基本上能够搞清楚tomcat的server.xml的每一个配置项都要做什么事情了。。。

那么当Server对象创建完成之后就可以进行接下来的初始化和启动过程了。。。。这部分的内容以后再说吧。。。

通过阅读的源码,可以将Tomcat服务器的结构大概用下图来描述:


一个服务器可能会定义多个server对象,每个server对象对应多个service对象,而每个service对象对应多个connector对象,每个service对象包含一个container对象。。。

大概的层次关系如上图。。。


http://www.tuicool.com/articles/7BVfEfv

0 0
原创粉丝点击