Tomcat 的结构很复杂,但是 Tomcat 也非常的模块化,找到了 Tomcat 最核心的模块,您就抓住了 Tomcat 的“七寸”。下面是 Tomcat 的总体结构图:
从上图中可以看出 Tomcat 的心脏是两个组件:Connector 和 Container,关于这两个组件将在后面详细介绍。Connector 组件是可以被替换,这样可以提供给服务器设计者更多的选择,因为这个组件是如此重要,不仅跟服务器的设计的本身,而且和不同的应用场景也十分相关,所以一个 Container 可以选择对应多个 Connector。多个 Connector 和一个 Container 就形成了一个 Service,Service 的概念大家都很熟悉了,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。
通过关键类来看Tomcat结构
当通过 ./startup.sh
脚本或直接通过 java
命令来启动 Bootstrap
时,Tomcat 的启动过程就正式开始了,启动的入口点就是 Bootstrap
类的 main
方法。
启动的过程分为三步,分别为:
1. Bootstrap初始化 Bootstrap bootstrap = new Bootstrap(); bootstrap.init(); daemon = bootstrap; 2. load和start daemon.load(args); daemon.start();
先贴出源码,在源码中标注了顺序,下面详解
/** * Initialize daemon. */ public void init() throws Exception { //1. Set Catalina path setCatalinaHome(); setCatalinaBase(); //2. 初始化classLoader initClassLoaders(); 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); //3. 设置parentClassLoader method.invoke(startupInstance, paramValues); // 4. 将catalinaDaemon赋值为Catalia实例 catalinaDaemon = startupInstance; }
贴出源码:
/** * Load daemon. */ private void load(String[] arguments) throws Exception { // Call the load() method String methodName = "load"; Object param[]; Class<?> paramTypes[]; if (arguments==null || arguments.length==0) { paramTypes = null; param = null; } else { paramTypes = new Class[1]; paramTypes[0] = arguments.getClass(); param = new Object[1]; param[0] = arguments; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method); method.invoke(catalinaDaemon, param); }
可以看出这个做的主要事情是调用catalinaDaemon.load()
在第一步init的时候 知道catalinaDaemon为Catalina的实例,所以看下catalina.load(),其中的关键性代码如下:
//1. Create and execute our Digester Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; // 2.读取配置文件conf/server.xml file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource(file.toURI().toURL().toString()); inputSource.setByteStream(inputStream); digester.push(this); // 3.解析配置文件 digester.parse(inputSource); getServer().setCatalina(this); // Stream redirection initStreams(); // 4.初始化server主要初始化声明周期 getServer().init();