上一篇文章初步看了一下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对象。。。
大概的层次关系如上图。。。
Tomcat源码阅读之Server.xml文件的处理与Catalina启动流程,布布扣,bubuko.com
Tomcat源码阅读之Server.xml文件的处理与Catalina启动流程
原文:http://blog.csdn.net/fjslovejhl/article/details/20051521