3.按功能看处理流程
将Bootstrap类按其代码分为三部分:
初始化部分,主要是初始化CATALINA_HOME 和CATALINA_BASE变量;
main方法部分一:创建和初始化daemon和catalinaDaemon、创建三个重要类加载器;
main方法部分二:控制Tomcat的启动与停止。
对应流程图如下:
2. 初始化CATALINA_HOME 和CATALINA_BASE
首先看一下Bootstrap类,最早会通过一段代码确定CATALINA_HOME 和CATALINA_BASE两个重要值:
private static final File catalinaBaseFile; private static final File catalinaHomeFile; private static final Pattern PATH_PATTERN = Pattern.compile("(\"[^\"]*\")|(([^,])*)"); static { // Will always be non-null String userDir = System.getProperty("user.dir"); // Home first String home = System.getProperty(Constants.CATALINA_HOME_PROP); //省略部分代码 catalinaHomeFile = homeFile; System.setProperty(Constants.CATALINA_HOME_PROP, catalinaHomeFile.getPath()); // Then base String base = System.getProperty(Constants.CATALINA_BASE_PROP); if (base == null) { catalinaBaseFile = catalinaHomeFile; } else { File baseFile = new File(base); try { baseFile = baseFile.getCanonicalFile(); } catch (IOException ioe) { baseFile = baseFile.getAbsoluteFile(); } catalinaBaseFile = baseFile; } System.setProperty( Constants.CATALINA_BASE_PROP, catalinaBaseFile.getPath()); }
**CATALINA_HOME:**代表Tomcat安装的根目录,例如/home/tomcat/apache-tomcat-9.0.10或C:\Program Files\apache-tomcat-9.0.10。
**CATALINA_BASE:**表示特定 Tomcat 实例的运行时配置的根。如果您想在一台机器上拥有多个 Tomcat 实例,请使用 CATALINA_BASE 属性。
**默认情况下,CATALINA_HOME 和 CATALINA_BASE 指向同一目录。**如果将属性设置为不同的位置,则 CATALINA_HOME 位置包含静态源,例如 .jar 文件或二进制文件。 CATALINA_BASE 位置包含配置文件、日志文件、部署的应用程序和其他运行时要求。
3. 创建并初始化守护进程daemon和catalinaDaemon
初始化完毕,接着执行的就是main方法,这个方法功能分为两部分:
synchronized (daemonLock) { if (daemon == null) { // 在初始化完成之前,不要对daemon赋值 Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { //当作为服务正在运行时,如果调用停止方法,这将在一个新线程上进行,以确保使用正确的类加载器,防止出现未找到类的异常。 Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } }
首先创建一个Bootstrap类型的对象,并调用其init()方法进行初始化,直至其初始化完毕,将其赋值给daemon对象。
重点在init方法:
public void init() throws Exception { //创建并初始化三个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.getConstructor().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 = startupInstance; }