2013-09-07
周末来啊,宅的日子又到了... 咱们知道tomcat的基本是容器,经过容器来完成servlet容器的设计。从上一篇blog中看到了tomcat的结构框架图,图中容器嵌套,组件组合,tomcat如何设置他们的结构关系,如何将他们融合成能完成Servlet容器更能的东西,经过对tomcat6.0.x启动过程的了解,咱们应该能够知道tomcat如何组织他们的。
要想知道tomcat的启动,首先得知道tomcat的启动类时哪个,跟踪启动脚本会发现启动类:org.apache.catalina.startup.Bootstrap,启动以后能够启用eclipse的单步跟踪分析启动过程。
org.apache.catalina.startup.Bootstrap,用来启动管理tomcat的运行,例如启动、中止等,在Bootstrap的main函数中咱们能够看到先判断有没有实例化过,而后调用init()函数,init()的代码java
public void init() throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); //构造所需的类加载器 initClassLoaders(); //设置当前线程的上下文类加载器为catalinaLoader Thread.currentThread().setContextClassLoader(catalinaLoader); //设置安全的类加载器为catalinaLoader SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); //使用catalinaLoader加载器加载类org.apache.catalina.startup.Catalina(提供Startup and Shutdown shell) Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); //设置startupInstance的父加载器为共享类加载器的 // Set the shared extensions class loader if (log.isDebugEnabled()) log.debug("Setting startup class properties"); String methodName = "setParentClassLoader"; Class paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; }
从代码中能够看出来再init()函数中完成了Servlet容器类加载器的设置,加载在web app用应用到的common、server、shared三个不一样的类加载器,设置类加载器是处于安全的考虑,例如一个Context不能直接访问另外一个Context,同时也是为了共享jar,在initClassLoaders()方法中完成了3个不一样类加载器对应目录下面的jar包,类文件的查找加载。还有一个很重要的就是实例化org.apache.catalina.startup.Catalina,并经过反射来设置org.apache.catalina.startup.Catalina的父类加载器。完成设置类加载和实例化org.apache.catalina.startup.Catalina后,根据传人参数来决定动做。在“start”参数选择启动servlet容器,会执行load(argments)方法和start()方法,load方法,负责调用org.apache.catalina.startup.Catalina.load()方法来加载配置文件,组织资源等,而后调用start方法来调用org.apache.catalina.startup.Catalina.start()方法,至此tomcat 完成启动。其实只要内容是在org.apache.catalina.startup.Catalina.load()中和org.apache.catalina.startup.Catalina.start()中。web
org.apache.catalina.startup.Catalina 是真正负责管理tomcat的类,主要是解析server.xml,初始化组件,操做组件状态,设置一个程序退出的Hook。Catalina 继承自Embedded,而Embedded又继承自StandardService,说明Catalina 是一个Service(这个有意思,难道tomcat只能有一个service,不太可能吧)。
在org.apache.catalina.startup.Bootstrap方法load()中调用了Catalina的load()方法,Catalina的方法load(argments):shell
public void load() { long t1 = System.nanoTime(); //catalina.home和catalina.base initDirs(); // Before digester - it may be needed //初始化命名 initNaming(); // Create and execute our Digester //建立将xml转成对象的解析规则器 Digester digester = createStartDigester(); //搜寻配置文件 InputSource inputSource = null; InputStream inputStream = null; File file = null; try { //加载conf/server.xml配置文件 file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); } catch (Exception e) { ; } if (inputStream == null) { try { inputStream = getClass().getClassLoader().getResourceAsStream( getConfigFile()); inputSource = new InputSource(getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception 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 ((inputStream == null) && (file != null)) { log.warn("Can't load server.xml from " + 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.parse(inputSource); inputStream.close(); } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": ", e); return; } // Stream redirection //设置输出流设备 initStreams(); // Start the new server //启动新server if (getServer() instanceof Lifecycle) { try { getServer().initialize(); } 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"); }
从方法中能够知道,主要完成如下几个操做:apache
其中重要点在Digester库来解析conf/server.xml和初始化Server上面。 Digester digester = createStartDigester();建立一个对象解析server.xml。
createStartDigester():数组
protected Digester 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); //设置ClassLoader确保在同一个jvm的命名空间中,能够相互访问 digester.setClassLoader(StandardServer.class.getClassLoader()); // Configure the actions we will be using //转化对象 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/")); digester.addRuleSet(ClusterRuleSetFactory .getClusterRuleSet("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)); digester.addRuleSet(ClusterRuleSetFactory .getClusterRuleSet("Server/Service/Engine/Cluster/")); long t2 = System.currentTimeMillis(); if (log.isDebugEnabled()) log.debug("Digester for server.xml created " + (t2 - t1)); return (digester); }
方法中设置了tomcat容器组件层次之间的关系,Server解析,Service解析,Engine解析,Host解析等,Digester 是一个规则解析引擎,根据设定的规则来解析server.xml文件,同时实例化对象,调用方法设置层次关系,很强大,可是这也增长了阅读的难度。tomcat
在设置规则完成后,调用下面代码执行解析动做安全
digester.push(this);//压入本类实例对象,提供解析生成容器组件组合宿主 digester.parse(inputSource);//解析server.xml
解析过程是很复杂的,能够设置断点跟踪查看。
在解析完成以后,tomcat调用getServer().initialize();对server初始化,Server的标准实现StandardServer,StandardServer方法initialize()负责对Server初始化,跟踪代码发现主要是对注册在Server下面的service数组进行初始化。
疑问:居然Server下面有service数组,那么Catalina这个也实现了service接口的类在tomcat中充当怎样的角色?简单的控制角色吗?那干啥要实现service接口?
能够经过验证catalina是不是Server中service数组的一员,直接打印对象对吧输出结果,在下一篇中验证。app
初始化完成后,返回load方法,在返回到Bootstrap中,而后调用start方法来启动容器,在start中调用Catalina中的start方法启动容器。
Catalina的start()方法:框架
public void start() { if (getServer() == null) { load(); } if (getServer() == null) { log.fatal("Cannot start server. Server instance is not configured."); return; } long t1 = System.nanoTime(); // Start the new server if (getServer() instanceof Lifecycle) { try { ((Lifecycle) getServer()).start(); } catch (LifecycleException e) { log.error("Catalina.start: ", e); } } long t2 = System.nanoTime(); if (log.isInfoEnabled()) log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); try { // Register shutdown hook //添加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); } } } catch (Throwable t) { // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } if (await) { await();//等待线程结束(在catalina单线程中,等待结束) stop(); } }
方法中先判断Server是否存在实例,若是不存在,掉load()方法,加载server.xml配置文件,解析建立对象,而后调用start()方法启动容器,在server的start()方法中,跟initialize()方法也有,也是调用service数组中service的initialize()方法。在启动容器后,添加一个Runtime.getRuntime().addShutdownHook(shutdownHook);监控程序异常退出时清理资源。
tomcat的启动有不少信息,service的initialize()方法和验证疑问以及service的start()方法会在接下来的blog中跟踪...
tomcat启动,首先设置类加载器,加载jar包和类,设置环境目录,使用Digester库解析server.xml生成容器组件层次关系,初始化Server对象,启动Server,完成启动过程,在这里尚未讲到ServerSocket部分,是Service中的内容,还有server.xml的文件内容,还有Server的角色扮演。eclipse
欢迎吐槽...
慢慢走下去,路就会清晰,再看一眼,在清晰一点