本节通过跟踪Tomcat的源码来分析Tomcat是如何启动及装配各个组件的。最好下载Tomcat的源码导入到Eclipse,这样方便跟踪。方法可参考:
http://www.cnblogs.com/huangfox/archive/2011/10/20/2218970.html
在Tomcat的启动脚本篇,我们分析过,当执行Start.bat文件时,最后实际调用的是BootStrap.java类。如下图:
如上图,实际调用BootStrap,并传递一个名为‘start’参数。
在BootStrap的主方法main中,主要做了一下几件事情:
1、实例化一个BootStrap,并调用其init方法。
2、调用load方法。
3、调用start方法。
在init方法中,BootStrap完成了一件重要的工作,就是根据Java反射,实例化了一个org.apache.catalina.startup.Catalina类。Init的主要代码如下:
initClassLoaders();//初始化catalinaLoader Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // 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是BootStrap类的一个私有变量 catalinaDaemon = startupInstance;
BootStrap的load方法,通过反射,调用Catalina类的load方法。在Catalina类的load方法中完成了一项极其重要的工作,就是通过Apache的另一项开源项目Digester来解析Tomcat的核心配置文件:conf/server.xml。 Digester作用是讲XML转成指定的Java对象。Catalina类的load方法的具体工作下面会介绍到。
再说BootStrap的start方法,主要是调用Catalina类的start方法。
通过对BootStrap三个方法的分析可以看到,BootStrap类启动时的主要工作就是实例化Catalina,调用其load方法与start方法。并且这些操作都是在BootStrap类中通过Java反射机制来完成的。
前面提到了Catalina的load方法通过Apache的另一项开源项目Digester来解析Tomcat的核心配置文件:conf/server.xml。Catalina定义了一个名为createStartDigester的方法,此方法根据server.xml的结构,定义了一套解析Server.xml文件的柜子,并返回一个实例化的Digester。在load方法使用返回的Digester示例加载server.xml配置文件,并进行解析。Load方法的主要代码如下:
Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; try { file = configFile();//根据catalina.base路径获取server.xml文件 inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); } catch (Exception e) { ; } //…….略去一些源码 try { inputSource.setByteStream(inputStream); //Catalina将自身设置进digester digester.push(this); digester.parse(inputSource); inputStream.close(); } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": " , e); return; } getServer().initialize();//初始化Server
在load方法中,通过调用digester.push(this); Catalina将自身设置进digester,这样通过digester.parse方法解析后,Catalina对象的各个属性将被实例化并填充。最关键的是实例化了Server对象。实际上digester实例化了Service、Connector、Engine、Host、context等各种组件,并相应的设置了各个组件的关系。
Load方法在最后调用了server的initialize方法。在Tomcat中Server接口的标准实现是StandardServer。具体是那个实现类,就看Catalina类createStartDigester方法里面定义的了。StandardServer的initialize方法对其子组件service进行了初始化。
// Initialize our defined Services for (int i = 0; i < services.length; i++) { services[i].initialize(); }
Service在Tomcat中的标准实现是StandardService。其initialize方法主要代码如下:
synchronized (connectors) { for (int i = 0; i < connectors.length; i++) { try { connectors[i].initialize(); } catch (Exception e) { String message = sm.getString( "standardService.connector.initFailed", connectors[i]); log.error(message, e); if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) throw new LifecycleException(message); } } }
可以看到Service的initialize主要工作就是调用connector的initialize方法。
同理Connector会依次处理其下的其他组件,这里不再依次列入。
让我们回到Catalina,再看看它的start方法:
if (getServer() instanceof Lifecycle) { try { ((Lifecycle) getServer()).start(); } catch (LifecycleException e) { log.error("Catalina.start: ", e); } }
StandardServer的start方法关键代码如下:
synchronized (services) { for (int i = 0; i < services.length; i++) { if (services[i] instanceof Lifecycle) ((Lifecycle) services[i]).start(); } }
同样的StandardService会依次启动其下的其他组件,此处不再依次列出。
等Catalina的Start方法执行完,表明Tomcat已经配置好自身,可以对外工作了。整个启动时序图如下:
上一篇:Tomcat 学习进阶历程之Tomcat架构与核心类分析
Tomcat 学习进阶历程之Tomcat启动过程分析,布布扣,bubuko.com
原文:http://blog.csdn.net/shiziaishuijiao/article/details/26135045