Tomcat7启动分析(二)Bootstrap类中的main方法
之前分析了Tomcat的启动脚本,如果从startup.bat开始启动Tomcat的话会发现最后会调用org.apache.catalina.startup.Bootstrap里的main方法,并且传过来的最后一个命令行参数是start,接下来的启动代码分析就从这里开始。
先看下这个main方法的代码:
1 /** 2 * Main method and entry point when starting Tomcat via the provided 3 * scripts. 4 * 5 * @param args Command line arguments to be processed 6 */ 7 public static void main(String args[]) { 8 9 if (daemon == null) { 10 // Don't set daemon until init() has completed 11 Bootstrap bootstrap = new Bootstrap(); 12 try { 13 bootstrap.init(); 14 } catch (Throwable t) { 15 handleThrowable(t); 16 t.printStackTrace(); 17 return; 18 } 19 daemon = bootstrap; 20 } else { 21 // When running as a service the call to stop will be on a new 22 // thread so make sure the correct class loader is used to prevent 23 // a range of class not found exceptions. 24 Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); 25 } 26 27 try { 28 String command = "start"; 29 if (args.length > 0) { 30 command = args[args.length - 1]; 31 } 32 33 if (command.equals("startd")) { 34 args[args.length - 1] = "start"; 35 daemon.load(args); 36 daemon.start(); 37 } else if (command.equals("stopd")) { 38 args[args.length - 1] = "stop"; 39 daemon.stop(); 40 } else if (command.equals("start")) { 41 daemon.setAwait(true); 42 daemon.load(args); 43 daemon.start(); 44 } else if (command.equals("stop")) { 45 daemon.stopServer(args); 46 } else if (command.equals("configtest")) { 47 daemon.load(args); 48 if (null==daemon.getServer()) { 49 System.exit(1); 50 } 51 System.exit(0); 52 } else { 53 log.warn("Bootstrap: command \"" + command + "\" does not exist."); 54 } 55 } catch (Throwable t) { 56 // Unwrap the Exception for clearer error reporting 57 if (t instanceof InvocationTargetException && 58 t.getCause() != null) { 59 t = t.getCause(); 60 } 61 handleThrowable(t); 62 t.printStackTrace(); 63 System.exit(1); 64 } 65 66 }
这里的daemon是Bootstrap类中的一个静态成员变量,类型就是Bootstrap,第10行的注释已经说明在调用过init方法之后才会给该变量赋值,初始时将是null,所以首先将实例化一个Bootstrap对象,接着调用init方法,该方法代码如下:
1 /** 2 * Initialize daemon. 3 */ 4 public void init() 5 throws Exception 6 { 7 8 // Set Catalina path 9 setCatalinaHome(); 10 setCatalinaBase(); 11 12 initClassLoaders(); 13 14 Thread.currentThread().setContextClassLoader(catalinaLoader); 15 16 SecurityClassLoad.securityClassLoad(catalinaLoader); 17 18 // Load our startup class and call its process() method 19 if (log.isDebugEnabled()) 20 log.debug("Loading startup class"); 21 Class<?> startupClass = 22 catalinaLoader.loadClass 23 ("org.apache.catalina.startup.Catalina"); 24 Object startupInstance = startupClass.newInstance(); 25 26 // Set the shared extensions class loader 27 if (log.isDebugEnabled()) 28 log.debug("Setting startup class properties"); 29 String methodName = "setParentClassLoader"; 30 Class<?> paramTypes[] = new Class[1]; 31 paramTypes[0] = Class.forName("java.lang.ClassLoader"); 32 Object paramValues[] = new Object[1]; 33 paramValues[0] = sharedLoader; 34 Method method = 35 startupInstance.getClass().getMethod(methodName, paramTypes); 36 method.invoke(startupInstance, paramValues); 37 38 catalinaDaemon = startupInstance; 39 40 }
这里不再逐句解释代码的作用,总的来说这个方法主要做了一下几件事:1.设置catalina.home、catalina.base系统属性,2.创建commonLoader、catalinaLoader、sharedLoader三个类加载器(建议看看createClassLoader方法,里面做的事情还挺多,比如装载catalina.properties里配置的目录下的文件和jar包,后两个加载器的父加载器都是第一个,最后注册了MBean,可以用于JVM监控该对象),3.实例化一个org.apache.catalina.startup.Catalina对象,并赋值给静态成员catalinaDaemon,以sharedLoader作为入参通过反射调用该对象的setParentClassLoader方法。
接下来去命令行最后一个参数,按文章开头所说是start,所以将执行34行到36行的代码,将会执行Bootstrap类中的load、start方法。
load方法代码如下:
1 /** 2 * Load daemon. 3 */ 4 private void load(String[] arguments) 5 throws Exception { 6 7 // Call the load() method 8 String methodName = "load"; 9 Object param[]; 10 Class<?> paramTypes[]; 11 if (arguments==null || arguments.length==0) { 12 paramTypes = null; 13 param = null; 14 } else { 15 paramTypes = new Class[1]; 16 paramTypes[0] = arguments.getClass(); 17 param = new Object[1]; 18 param[0] = arguments; 19 } 20 Method method = 21 catalinaDaemon.getClass().getMethod(methodName, paramTypes); 22 if (log.isDebugEnabled()) 23 log.debug("Calling startup class " + method); 24 method.invoke(catalinaDaemon, param); 25 26 }
就是通过反射调用catalinaDaemon对象的load方法,catalinaDaemon对象在上面的init方法中已经实例化过了。
start方法与load方法相似,也是通过反射调用catalinaDaemon对象上的start方法:
1 /** 2 * Start the Catalina daemon. 3 */ 4 public void start() 5 throws Exception { 6 if( catalinaDaemon==null ) init(); 7 8 Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null); 9 method.invoke(catalinaDaemon, (Object [])null); 10 11 }
下面一篇文章将分析catalinaDaemon对象中的load、start两个方法,里面会涉及一个有趣的话题——Digester的使用。