Tomcat的启动类是Bootstrap,它承担着管理Catalina的责任.看其main方法可以知道它首先是实例化一个Bootstrap实例,这是一个主线程.该实例创建后立即进行初始化,次调用是一个很复杂的过程.它包含了Tomcat的很多初始化工作,接着是把该实例赋值给静态变量daemon.然后才是执行相应的启动或者停止命令.
首先,看看Bootstrap的init方法,代码如下:
setCatalinaHome(); setCatalinaBase(); 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); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance;
setCatalinaHome方法是设置系统变量catalina.home.如果系统中没有设置这个变量的话默认是当前的工作目录.setCatalinaBase是设置变量catalina.base,与setCatalinaHome方法类似.initClassLoaders是初始化类加载器.这些类加载器是通过配置文件来进行配置的,本文稍后进行解析.接着是实例化org.apache.catalina.startup.Catalina这个类,并设置它的类加载器.这个类才是Tomcat提供服务的地方.Bootstrap不过是为了外部对Catalina进行控制的媒介.我们可以看到Bootstrap执行start或者stop等方法都是对catalinaDaemon进行操作的,也就是控制catalina的启动或者停止.
commonLoader = createClassLoader("common", null); if( commonLoader == null ) { // no config file, default to this loader - we might be in a 'single' env. commonLoader=this.getClass().getClassLoader(); } catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader);
上述代码是initClassLoaders的主体它是调用createClassLoader来创建ClassLoader的.其主要代码如下
String value = CatalinaProperties.getProperty(name + ".loader"); if ((value == null) || (value.equals(""))) return parent; value = replace(value); List<Repository> repositories = new ArrayList<Repository>(); StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { String repository = tokenizer.nextToken().trim(); if (repository.length() == 0) { continue; } // Check for a JAR URL repository try { @SuppressWarnings("unused") URL url = new URL(repository); repositories.add( new Repository(repository, RepositoryType.URL)); continue; } catch (MalformedURLException e) { // Ignore } // Local repository if (repository.endsWith("*.jar")) { repository = repository.substring (0, repository.length() - "*.jar".length()); repositories.add( new Repository(repository, RepositoryType.GLOB)); } else if (repository.endsWith(".jar")) { repositories.add( new Repository(repository, RepositoryType.JAR)); } else { repositories.add( new Repository(repository, RepositoryType.DIR)); } } ClassLoader classLoader = ClassLoaderFactory.createClassLoader (repositories, parent); // Retrieving MBean server MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = ManagementFactory.getPlatformMBeanServer(); } // Register the server classloader ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader;
CatalinaProperties对应着Tomcat目录下的catalina.propertis.默认的配置有common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar也就是配置了commonClassLoader的类路径.这里就指定了Catalina所能load的类的路径.因为init在实例化Catalina实例后调用了setParentClassLoader把initClassLoaders中初始化好的自定义ClassLoader类设置给Catalina.
经过上述步骤后Bootstrap基本上已经初始化好,接下来就是命令行中传入的是何种命令,执行相应的方法.
String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[args.length - 1] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[args.length - 1] = "stop"; daemon.stop(); } else if (command.equals("start")) { daemon.setAwait(true); daemon.load(args); daemon.start(); } else if (command.equals("stop")) { daemon.stopServer(args); } else if (command.equals("configtest")) { daemon.load(args); if (null==daemon.getServer()) { System.exit(1); } System.exit(0); } else { log.warn("Bootstrap: command \"" + command + "\" does not exist."); }
默认执行的是start命令,也就是调用了Bootstrap的start方法.该方法是启动Tomcat连接器和容器.在以后的博文中进行解读.