一.将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在启动时做的部分工作。
相关推荐
总之,学习Tomcat源码不仅能够提升你对Web服务器底层工作的理解,还能够提高你在Java Web开发中的问题排查能力,让你成为一名更出色的开发者。通过阅读《How Tomcat Works》并结合实际项目实践,你将能够逐步揭开...
【标题】:“Web学习笔记——Tomcat” 在Web开发领域,Tomcat是一个广泛使用的Java Servlet容器,它实现了Java EE中的Web部分,特别是Servlet和JSP规范。Tomcat以其开源、轻量级和高效的特性,成为了许多小型项目和...
总的来说,Tomcat源码分析涉及了从启动流程到核心组件的各个方面,深入学习这些内容能够提升开发者对Web服务器的理解,从而提高开发和维护效率。通过对源码的解读,我们可以更有效地解决性能瓶颈、优化配置,以及...
**Tomcat源码研究** Tomcat是一款开源的Java Servlet容器,是Apache软件基金会下的Jakarta项目的一部分。它被广泛用于部署Web应用程序,因为其轻量级、高效且易于理解和定制的特性。以下是对Tomcat源码研究的一些...
这个"spring学习笔记,包括源码学习"的资料很可能包含了一系列关于Spring框架的核心概念、配置、使用方法以及深入源码的解析。 首先,让我们来了解一下Spring框架的基础知识。Spring的核心特性是依赖注入,它允许...
7. IDEA 安装JREBEL插件 7 8. IDEA不能实时编译的一个临时解决办法 8 8.1. 可以用(CTRL+S和CTRL+F9)进行保存和编译 8 8.2. 通过IDEA录制宏的功能,内容是CTRL+S和CTRL+F9 8 9. IDEA 设置代码提示或自动补全的...
首先,Tomcat启动时会读取配置文件(如server.xml),这些配置文件定义了服务器的各个组件,如Connectors、Catalina(核心引擎)、Hosts、Contexts等。Connectors负责监听网络端口,接收HTTP请求,并将其传递给...
这个"springboot学习笔记源码"包含了基于JSP、JPA和FreeMarker的实践项目,非常适合初学者或者希望深入理解SpringBoot集成这些技术的开发者进行学习。 1. **SpringBoot核心概念** SpringBoot的核心特性包括自动...
SpringBoot经典学习笔记是针对Java开发者的宝贵资源,它涵盖了SpringBoot的核心概念、特性以及实践应用。SpringBoot是由Pivotal团队开发的框架,旨在简化Spring应用的初始搭建以及开发过程,通过“约定优于配置”的...
在Tomcat的`conf/server.xml`文件中,添加一个Listener来启动OpenEjb。例如: ``` <Listener className="org.apache.openejb.tomcat.OpenEjbSystemLoader" /> ``` 3. **配置EJB项目**: 创建一个EJB项目,包含...
### Linux学习笔记知识点详解 #### 一、用户与权限管理 **1. 切换当前用户** - **命令格式**: `su [要切换的用户名]` - **示例**: `su root` - **解释**: 使用`su`命令可以切换到另一个用户身份进行操作。如果...
Spring Boot 是一个基于 Spring 框架的快速开发...这个完整的 Spring Boot 学习笔记涵盖了从基础到进阶的各个方面,通过学习和实践,你可以掌握 Spring Boot 开发的核心技能,从而高效地构建高质量的 Java 后端应用。
**SpringBoot学习笔记** SpringBoot是由Pivotal团队开发的一款基于Java的轻量级框架,旨在简化Spring应用的初始搭建以及开发过程。它通过自动配置、起步依赖和运行时嵌入式服务器,使得开发者能够快速地创建独立的...
总结来说,SpringBoot学习笔记和实践源码是理解并掌握Spring Boot框架的重要资源。通过理论学习与实际操作相结合,开发者能够迅速上手Spring Boot,从而提升开发效率,构建出健壮、易维护的Java应用。
通过学习和实践"达内Java项目云笔记12天完整源码",开发者可以深化对Java编程、Web开发以及现代软件工程实践的理解,提升自己的技能水平。这个项目不仅提供了丰富的代码示例,也是实战训练的好材料。
7. **Tomcat启动过程**:《Tomcat启动过程分析.mht》揭示了Apache Tomcat服务器启动的内部机制,包括配置解析、类加载、Servlet初始化等,对于理解Web应用的运行环境很有帮助。 8. **URL编码**:《对比 javascript ...
SpringBoot是Spring框架的一种简化和快速开发方式,它...在阅读提供的源码笔记时,应重点关注SpringBoot的启动流程、自动配置机制以及如何集成和配置各种服务。同时,理解并运用最佳实践,能够提升代码质量和应用性能。
在"Servlet&JSP学习笔记源代码"中,林信良教授通过实例讲解了这两项技术的基础和进阶知识。这些源代码是在Eclipse集成开发环境中创建的,Eclipse是Java开发的主流工具,支持丰富的插件,使得开发、调试和部署Servlet...