其中conf中有一些比较重要的文件,以下:java
catalina.properties | Tomcat环境变量配置 |
catalina.policy | 安全模式下运行时的默认安全策略 |
context.xml | 全部web应用须要加载的Context配置,若是用户指定了本身的Context,这个文件将会被覆盖 |
logging.properties | Tomcat日志文件,能够修改tomcat的日志级别 |
Server.xml | Tomcat的很很重要的配置文件,配置链接器,监听端口,服务器实例等,tomcat优化相关也均会配置在此文件中 |
tomcat-users.xml | Tomcat manager认证 |
web.xml | Tomcat默认的web.xml文件,定义了基础的Servlet和MIME映射。若是应用中没有web.xml,则tomcat会使用此文件做为默认文件 |
当咱们下载下来tomcat之后,在bin文件夹下,tomcat分别提供了linux和windows的启动脚本文件,startup.bat和startup.sh,这个文件没有实质性的用处,主要是为了引导执行catalina.sh(或者catalina.bat)文件,因此咱们在启动tomcat时,也能够直接启动catalina.sh。这个文件内容特别多,有500多行,是tomcat启动的核心文件。主要分为如下几方面内容linux
1. 设置CATALINA_HOME和CATALINA_BASEweb
2. 寻找是否有setevn.sh和setclasspath.sh文件,并加载,咱们自定义的tomcat参数,例如设置jvm参数的大小,就能够在新建一个setevn.sh文件,而后在这个文件中配置,tomcat会自动加载这个文件。setclasspath.sh是一些必要的批处理脚本,它只负责查找和检查JAVA_HOME和JRE_HOME两个环境变量。apache
3. 设置日志组件。windows
4. 根据不一样的配置组装最后要启动的命令参数。设计模式
5. 执行启动命令tomcat
tomcat的启动类是Bootstrap.java,类首先经过静态方法加载catalina.base和catalina.home的位置,而后实例化Bootstarp类,经过调用init方法进行初始化。初始化时,加载安全
${catalina.base}/lib,${catalina.base}/lib/*.jar,"${catalina.home}/lib,${catalina.home}/lib/*.jar 这些文件夹以及jar文件。并建立tomcat原始的ClassLoader。而后经过classLoader实例化org.apache.catalina.startup.Catalina类并进行初始化其parentClassLoader,而后此函数线程做为守护线程。服务器
默认的启动参数是start,当参数是start时表明是启动tomcat,其实是调用Catalina.java的load()和start()函数进行启动。接下来分别看一下这两个函数框架
load函数会实例化出来一个Server实例。每一个tomcat只能有一个server实例。server节点是server.xml的根节点,tomcat全部的配置也都是在这这个节点之下,下属节点主要分为链接器,监听器,容器这几类。
public void load() { long t1 = System.nanoTime(); //设置临时文件目录 initDirs(); // 初始化digester框架可能用的一些naming信息 initNaming(); // 建立Digester对象,而且作节点和相关实体类的映射对照关系 Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { //巴拉巴拉一大堆,就是为了加载server.xml文件到digester中 } try { inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); } catch (SAXParseException spe) { } } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { // Ignore } } } /** 初始化Server的一些最基本的信息 server指的是org.apache.catalina.Server,这个也就是tomcat的顶级容器 **/ getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); try { /** * 初始化Server信息,Server是一个接口,默认实现是StandardServer * StandardServer继承自抽象类LifecycleBase,init方法由其实现,tomcat的容器的默认实现均继承自此抽象类,而且实现lifecycle接口,LifecycleBase中有抽象方法startInternal()由其子类进行实现 * 具体的针对tomcat容器的启动方式稍后再作分析 */ getServer().init(); } 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); } }
public void start() { //重复的加载server,以保证server不是空的,重复加载之后若是server依然为空,则抛出异常,启动失败 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 try { //启动server容器 getServer().start(); } 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(); } //增长关闭钩子,tomcat在关闭(无论是正常仍是异常)时执行后续的收尾工做 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) { //开启ServerSocket,等待请求,并接收请求 await(); //去除关闭钩子,逐级关闭容器 stop(); } }
tomcat有四种容器,这个经过server.xml能够看出来
容器均实现Lifecycle接口,它的启动,是在经过调用StandardServer load方法时进行初始化,经过调用start方法进行启动。在Lifecycle的开头注释部分,很是明确的画出了整个容器的声明周期过程。
首先容器须要先调用init()方法进行初始化,初始化完成以后调用start()方法启动容器,当容器再也不使用,调用stop()进行中止, 最后destory()方法销毁容器
在启动的过程当中,因为涉及到不少的事件,对于各类事件的处理不可能硬编码到代码中,而且了为了可扩展,lifecycle定义了不少的事件监听点,用到自定义实现的观察者设计模式。各个事件的监听器能够注册本身感兴趣的事件,当这事件发生时会进行通知。
生命周期时期 |
调用事件 |
init |
在init以前会调用先后会分别调用注册了如下事件的方法 BEFORE_INIT_EVENT AFTER_INIT_EVENT |
start |
在接收到start命令时,调用START_EVENT Start以前调用BEFORE_START_EVENT,容器启动以后调用注册了AFTER_START_EVENT事件的方法。 |
stop |
接收到stop命令,会调用STOP_EVENT的事件, Stop以前调用BEFORE_STOP_EVENT,以后调用AFTER_STOP_EVENT |
destroy |
容器销毁以前调用AFTER_DESTROY_EVENT,销毁以后调用感兴趣的注册了BEFORE_DESTROY_EVENT事件的类的方法 |
除了以上还提供了PERIODIC_EVENT事件,这个注册此时间的方法会被周期性的执行,配合CONFIGURE_START_EVENT和CONFIGURE_STOP_EVENT事件的使用,能够监控配置文件是否修改,而后进行热部署的效果。
tomcat容器的启动采用的是链式启动结构,有父容器进行查找其下面的字容器进行启动。例如咱们启动Engine,它会自动的找它下面的Host,启动Engine的同时进行启动host。
容器均实现Container接口, ContainerBase,以Standardxxx开头的类是容器的默认实现,Container接口中有一个findChinldren()方法,用来查找这个容器下的子容器,默认由ContainerBase实现,container接口有一个默认的抽象实现类ContainerBase实现,而findChinldren()方法也默认由其实现。容器的默认实现Standardxxx 继承自ContainerBase。
而整个tomcat的启动的概要流程图能够表示以下: