tomcat7源码学习笔记(启动篇)
一.将tomcat源码导入到eclipse
??? 在apach官网上下载tomcat源码后,学习源码通过导入到eclipse里,然后用其调试功能来学习是的方便。对于eclipse导入tomcat源码可以参考:http://www.cnblogs.com/huangfox/archive/2011/10/20/2218970.html 对于tomcat依赖的jar,可以通过后面的附件来下载。
?
?
二.运行tomcat
??? 首先,tomcat在启动时,会读取一些配置文件,也就是${CATALINA_HOME}/conf下面的所有配置文件,我们可以将conf文件夹及其里面的所有配置文件拷到eclipse中来,提供tomcat启动需要的配置信息。
??? 在org.apache.catalina.startup这个包下面的Bootstrap.java是启动tomcat的类,它里面有个main函数,是启动tomcat的入口,代码如下
?
/** * Main method and entry point when starting Tomcat via the provided * scripts. * * @param args Command line arguments to be processed */ public static void main(String args[]) { if (daemon == null) { // Don't set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { // When running as a service the call to stop will be on a new // thread so make sure the correct class loader is used to prevent // a range of class not found exceptions. Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } try { 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."); } } catch (Throwable t) { // Unwrap the Exception for clearer error reporting if (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1); } }
??????? 通过上面源码可以看出,tomcat先进行初始化,主要进行的功能有:设置catalina的路径(主要有catalina.home和catalina.base这两个路径),初始化catalina的加载类(主要有三个类别:common.loader,server.loader,shared.loader,这些加载类都是通过读取conf下面的catalina.properties配置文件来获取相应的加载类,而且这些类加载都是通过tomcat里设置的安全验证的),设置catalina的(这个类的路径为:org.apache.catalina.startup.Catalina。其中默认加载类为org.apache.catalina.loader.StandardClassLoader,加载时需要的配置文件路径为:conf/server.xml)。?????? tomcat在初始化完上面的功能后,就根据启动时的命令参数,来进行相应命令的动作,通过上面的源码可以看出,在启动Bootstrap.java这个类时,可以传入的命令参数有:startd(默认值)、stopd、configtest,具体相应命令的功能,就不再进行说明。
?
?
三,分析init初始化哪些内容??
??? 下面来具体看看tomcat在启动时,都初始化了哪些内容。首先看一下init()这个方法的源码:
/** * Initialize daemon. */ public void init() throws Exception { // Set Catalina path setCatalinaHome(); log.info("catalina.home:" + System.getProperty(Globals.CATALINA_HOME_PROP)); setCatalinaBase(); log.info("catalina.base:" + System.getProperty(Globals.CATALINA_BASE_PROP)); 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; }
???
??? 3.1 catalina的两个路径设置
??? 其中,setCatalinaHome()和setCatalinaBase();这两个方法就是设置catalina的两个路径的,具体的实现,首先参看setCatalinaHome()方法的源码:
/** * Set the <code>catalina.home</code> System property to the current * working directory if it has not been set. */ private void setCatalinaHome() { if (System.getProperty(Globals.CATALINA_HOME_PROP) != null) return; File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar"); if (bootstrapJar.exists()) { try { System.setProperty (Globals.CATALINA_HOME_PROP, (new File(System.getProperty("user.dir"), "..")) .getCanonicalPath()); } catch (Exception e) { // Ignore System.setProperty(Globals.CATALINA_HOME_PROP, System.getProperty("user.dir")); } } else { System.setProperty(Globals.CATALINA_HOME_PROP, System.getProperty("user.dir")); } }
??? 通过上面的代码可以看出,它设置的catalina.home的值,就是获取系统user.dir属性的值,也就是你当前eclipse导入tomcat源码建的项目的路径,如果你项目路径如下所示:
?????? 它的location在E:\study\tomcat7下面,那么System.getProperty("user.dir")的值也是E:\study\tomcat7.因为我们导入的源码中不会有bootstrap.jar,所以它在E:\study\tomcat7\bootstrap.jar是不存在的,最后setCatalinaHome()这个方法执行的代码是:
else { System.setProperty(Globals.CATALINA_HOME_PROP, System.getProperty("user.dir")); }
?????? 也就是将catalina.home的值设置成为:E:\study\tomcat7。对于setCatalinaBase()的源码类似,就不再贴出,分析。
?
?????? 3.2? 初始化作为catalina的加载类
????? 对于这个功能,我们可以参看initClassLoaders()这个方法的源码:
private void initClassLoaders() { try { 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); } catch (Throwable t) { handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); } }
? ? ? ? 通过源码,在这个方法中我们看不到实质的内容,它们主要都是通过createClassLoader(String name, ClassLoader parent)这个方法来创建三个类型(commonLoader、catalinaLoader、sharedLoader)的加载类的。那我们就通过源码来看下这个方法的功能:
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { 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这个类,来获取相应要加载进来的jar包,然后通过调用replace(),来定位到相应的jar包,也就是把jar包的路径解析出来。然后,通过jmx将这些jar包的组件注册到tomcat中来;
??????? 对于这个方法中用到的CatalinaProperties,这个类主要是读取catalina配置的,这个类首先会执行静态块中的loadProperties()方法
/** * Load properties. */ private static void loadProperties() { InputStream is = null; Throwable error = null; try { String configUrl = getConfigUrl(); if (configUrl != null) { is = (new URL(configUrl)).openStream(); } } catch (Throwable t) { handleThrowable(t); } if (is == null) { try { File home = new File(getCatalinaBase()); File conf = new File(home, "conf"); File propsFile = new File(conf, "catalina.properties"); is = new FileInputStream(propsFile); } catch (Throwable t) { handleThrowable(t); } } if (is == null) { try { is = CatalinaProperties.class.getResourceAsStream ("/org/apache/catalina/startup/catalina.properties"); } catch (Throwable t) { handleThrowable(t); } } if (is != null) { try { properties = new Properties(); properties.load(is); is.close(); } catch (Throwable t) { handleThrowable(t); error = t; } } if ((is == null) || (error != null)) { // Do something log.warn("Failed to load catalina.properties", error); // That's fine - we have reasonable defaults. properties=new Properties(); } // Register the properties as system properties Enumeration<?> enumeration = properties.propertyNames(); while (enumeration.hasMoreElements()) { String name = (String) enumeration.nextElement(); String value = properties.getProperty(name); if (value != null) { System.setProperty(name, value); } } }
?????? 因为,这时还没有设置catalina.config系统属性,所以String configUrl = getConfigUrl();获取不到值,代码会接着执行:
File home = new File(getCatalinaBase()); File conf = new File(home, "conf"); File propsFile = new File(conf, "catalina.properties"); is = new FileInputStream(propsFile);
???? 通过这可以看出是读取conf/catalina.properties这个属性配置文件,然后通过下面代码加载到属性文件中
if (is != null) { try { properties = new Properties(); properties.load(is); is.close(); } catch (Throwable t) { handleThrowable(t); error = t; } }
????? 所以,通过以上的代码可以看出CatalinaProperties这个类最终读取的是conf/catalina.properties配置文件,这样在Bootstrap.java这个类中的createClassLoader(String name, ClassLoader parent)方法的第一句
String value = CatalinaProperties.getProperty(name + ".loader");
????? 读取的值就是conf/catalina.properties里面配置的值。这个方法后面的代码就是对于加载进行来conf/catalina.properties配置中的server.loader、common.loader、shared.loader三个值中的组件的注册工作。到些初始化catalina类的加载类分析工作已经完成,那接着看初始catalina。
?
???? 3.3 初始化catalina
????? 初始化catalina类的代码主要是Bootstrap.java类中init()方法后面的代码:
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;
?? ? ?? 首先通过前面已经初始化好的catalina的加载类,也就是org.apache.catalina.loader.StandardClassLoader来加载org.apache.catalina.startup.Catalina这个类,然后设置org.apache.catalina.startup.Catalina类的parentClassLoader为org.apache.catalina.loader.StandardClassLoader(这其中用到反射机制)。
?
?????? 通过以上的简单讲解,让大家大致了解到tomcat在启动时做的部分工作。
???