综述:在上一节中分析了Tomcat的执行过程。这一节将分析,Tomcat启动以及请求处理过程中所涉及到的类与对象,是由谁加载的,Tomcat的类加载器的特点。
4.1 JAVA的类加载过程
1)类加载load:从字节码二进制文件.class文件将类加载到内存将内存中的class放到运行时数据区的方法区内。类的初始化过程会在堆区建立一个java.lang.Class对象,用来封装该类相关的数据结构。
2) 连接:连接又分为以下小步骤
a) 验证:出于安全性的考虑,验证内存中的字节码是否符合JVM的规范,类的结构规范、语义检查、字节码操作是否合法、这个是为了防止用户自己建立一个非法的XX.class文件就进行工作了,或者是JVM版本冲突的问题,比如在JDK6下面编译通过的class(其中包含注解特性的类),是不能在JDK1.4的JVM下运行的。
b) 准备:将类的静态变量进行分配内存空间、初始化默认值。(对象还没生成呢,所以这个时候没有实例变量什么事情)
c) 解析:不同的JVM实现可能选择不同的解析策略。一种做法是递归的把所有依赖的形式引用(只是申明,并不适用类)都进行解析。而另外的做法则可能是只在一个形式引用真正需要的时候(也就是说在所引用类的对象被创建)才进行解析。也就是说如果一个Java类只是被引用了,但是并没有被真正用到,那么这个类有可能就不会被解析。当前的JVM一般都采用了第二种策略。
3) 类的初始化: 将类的静态变量赋予正确的初始值,这个初始值是开发者自己定义时赋予的初始值,而不是默认值。
4.2 类加载器的加载模式
Parent First加载模式(也就是在加载的时候会委托给父亲类加载器优先加载的模式)只是JVM的一种默认实现,并不是所有的类加载都需要实现该模式的。
Parent Last加载模式,Tomcat的应用类加载器,WebappClassLoader,每一个Context都会尝试优先加载所需要的类,只有在无法加载到的时候才委托给父亲加载器完成加载操作。之所以实现这样一种策略为了在多应用的环境中,例如应用A,B使用了默认的数据库连接实现,而应用C需要定制,那么这样C应用中只要包含该实现就可以实现覆盖默认的实现。
4.3 Tomcat7的类加载器的层级结构
Tomcat7运行时类的加载说明:
1)Bootstrap Class Loader是JVM的内核由C实现的,加载了JVM的核心包rt.jar。rt.jar中的所有类执行其class的getClassLoader()方法都将返回null,例如String.class.getClassLoader()。
2)Extension Class Loader主要加载了JVM扩展包中相关的jar包。例如运行下列代码将System.out.println(ZipPath.class.getClassLoader());将得到如下的运行结果:sun.misc.Launcher$ExtClassLoader
3)System Class Loader加载CLASSPATH相关的类,例如在Tomcat的Bootstrap的main方法中执行System.out.println(Bootstrap.class.getClassLoader());则将得到:sun.misc.Launcher$AppClassLoader
4)Common Class Loader,Tomcat7中的CATALINA_HOME/lib下的jar包。注意Tomcat在启动文件中将启动时配置了-classpath "%CATALINA_HOME%\lib\catalina.jar"因此catalina.jar中的类虽然指定使用类加载器Common Class Loader,但是按JVM的委托加载原则System.out.println(Bootstrap.class.getClassLoader());得到的类加载器是:sun.misc.Launcher$AppClassLoader。
5)Webapp Class Loader, 主要负责加载Context容器中的所有的类。实际上该加载器提供了参数delegateLoad供用户设定是否使用parent-first加载。默认该值为false,默认用parent-last加载。出于安全性的考虑对于核心类WebappClassLoader是不允许加载的。包括:java.,javax.servlet.jsp.jstl,javax.servlet.,javax.el
4.4 类加载器的相关类结构图
4.5 类加载器的相关源代码
(一)ClassLoader的load方法
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
1)在该方法中,首先检查是否已经加载了该类,这里有个问题JVM如何判断一个类是否被加载过的?这里涉及到了类的命名空间问题。在JAVA中判断一个类是否相同不仅看类名是否相同,还要看其类加载器是否相同。同一个类可以被不同的类加载器所加载,并且认为是不同的。该问题可以分解下面两个方面看。
a) 单一加载原则:在加载器链中,一个类只会被链中的某一个加载器加载一次。而不会被重复加载。实现类的共享,Tomcat多个应用,如果需要共享一些jar包,那么只需要交给commonClassLoader加载,那么所有的应用就可以共享这些类。
b) 可见性原则:父加载器加载的类,子加载器是可以访问的。而自加载器所加载的类,父加载器无法访问。不同加载器链之间其是相互不可见,无法访问的。实现隔离,Tomcat就是应用该特性,为每一个Context容器创建一个WebappClassLoader类加载器对象,从而实现了应用间的相互隔离。应用间的类是不可见的所以无法相互访问。
2) 如果步骤一中无缓存,查看该类父加载器,如果存在那么委托给付加载器。如果没有父加载器那么认为BootstrapClassLoader是其父加载器,委托进行加载。
3)如果父加载器无法加载则抛出ClassNotFoundException,调用抽象方法findClass方法。
4)此处的resolveClass方法指的是上文类加载过程中连接的第三步操作。resolve该类的形式引用等等。
(二)类URLClassLoader的findClass方法
protected Class<?> findClass(final String name) throws ClassNotFoundException { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<Class>() { public Class run() throws ClassNotFoundException { String path = name.replace('.', '/').concat(".class"); Resource res = ucp.getResource(path, false); if (res != null) { try { return defineClass(name, res); } catch (IOException e) { throw new ClassNotFoundException(name, e); } } else { throw new ClassNotFoundException(name); } } }, acc); } catch (java.security.PrivilegedActionException pae) { throw (ClassNotFoundException) pae.getException(); } }
该方法的核心是获取到JAVA类的字节码,然后调用父类的defineClass方法完成类的构造过程。defineClass是由JVM实现的,不允许被覆写,因此用户类文件就必须遵循JVM的文件规范才能被正确的解析。
(三)WebappClassLoader重新覆写了ClassLoader的loadClass方法(删除了部分代码)
@Override public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class<?> clazz = null; ……… // (0) 当前对象缓存中检查是否已经加载该类 clazz = findLoadedClass0(name); // (0.1) 检查JVM的缓存,是否已经加载过该类 clazz = findLoadedClass(name); // (0.2) 防止加载一些系统相关的类 try { clazz = system.loadClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) {} boolean delegateLoad = delegate || filter(name); // (1) 如果配置了parent-first模式,那么委托给父加载器 if (delegateLoad) { ClassLoader loader = parent; if (loader == null) loader = system; try { clazz = Class.forName(name, false, loader); if (clazz != null) { if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) {} } // (2) 从应用环境中查找类,主要是应用下的/lib目录与classes目录 try { clazz = findClass(name); if (clazz != null) { if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) {} // (3) 如果在当前应用下无法找到所需要的类,再委托给父加载器加载(parent-last) if (!delegateLoad) { ClassLoader loader = parent; if (loader == null) loader = system; try { clazz = Class.forName(name, false, loader); if (clazz != null) { if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) {} } throw new ClassNotFoundException(name); }
4.6 总结
本节介绍了JVM的类加载器原理,Tomcat的类加载器结构,类加载器的实现,以及类加载器的部分源代码。重点需要理解的是类加载器的命名空间这一设计特性。所以的其他内容都是服务该特性的,从而实现了类加载的安全性,可见性等。
相关推荐
1. **Bootstrap ClassLoader**:这是JVM启动时的第一个类加载器,负责加载JDK核心库(rt.jar)中的类。 2. **Extension ClassLoader**:加载JRE扩展目录(java.ext.dirs)下的JAR文件。 3. **System ClassLoader**...
《深入剖析Tomcat7源码》 Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Web应用服务器标准,尤其是Servlet和JSP规范。源码下载是开发者深入理解其内部工作原理的重要途径,本篇文章将围绕...
这个"DevLoader.zip"文件可能包含与Tomcat自定义类加载器相关的资料,特别是名为"DevLoader"的类加载器,这可能是Tomcat为开发者提供的一种特殊加载器。 首先,我们来理解一下类加载器的基本概念。在Java中,类加载...
《深入剖析Tomcat7源码》 Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Servlet和JSP规范。源码分析是提升开发者对服务器内部运作机制理解的重要途径,尤其对于Tomcat这样的核心组件,源码的...
3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4
本篇将聚焦于"Tomcat源码阅读(一)——环境搭建",探讨如何搭建一个适合源码学习的开发环境。 首先,我们需要了解Tomcat是什么。Tomcat是一款开源的Java Servlet容器,由Apache软件基金会维护,实现了Java EE中的...
源码解析部分则是对Tomcat源码的深度剖析,涵盖了关键类和方法的作用、设计模式的运用以及性能优化技巧。这有助于开发者理解Tomcat内部的工作流程,例如,如何处理HTTP请求的生命周期,以及线程池是如何调度和管理的...
下面我们将深入探讨Java类加载器以及Tomcat中的类加载器。 在Java中,类加载器主要分为三个层次:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader。Bootstrap ClassLoader负责加载JDK的核心库,如rt...
这里我们讨论的主题是"Tomcat7源码环境部署",这涉及到对Tomcat服务器的深入理解,包括其内部工作原理、源码分析以及如何在本地环境中构建和部署。下面将详细介绍相关知识点。 首先,源码部署意味着你需要从Apache ...
3. **类加载器机制**:Tomcat的类加载器设计独特,理解它如何加载Web应用的类,以及不同Web应用之间的隔离性是重要的一环。 4. **部署和生命周期管理**:了解Tomcat如何解析并部署WAR文件,以及Web应用的启动、停止...
Tomcat使用自定义的类加载器来加载Web应用中的类,以实现不同应用之间的类隔离。主要有CommonClassLoader、SharedClassLoader和WebappClassLoader,它们按层次加载类。 5. **连接器(Connector)与协议处理器...
Tomcat7源码还涉及到了类加载机制,它采用了自定义的类加载器,如`CatalinaClassLoader`和`SharedClassLoader`,理解这些类加载器的工作方式有助于解决类冲突和加载顺序问题。 此外,Tomcat的安全管理也是重要一环...
tomcat工作原理深入详解——HowTomcatWorks中文版.pdf
下面将详细介绍Tomcat 7及其源码的相关知识点。 1. **Tomcat结构与组件**: - **Catalina**:核心组件,负责Servlet容器的主要功能,处理HTTP请求和响应。 - ** Coyote**:处理网络连接器和协议处理器,如处理...
7. **连接器(Connector)**:Tomcat支持多种协议,如HTTP/1.1、AJP等。`Coyote`组件负责处理这些协议,其中` CoyoteConnector`是核心部分。 8. **JSP和 Jasper**:Tomcat内置了JSP引擎Jasper,负责编译JSP页面为...
源码阅读可以帮助我们理解其内部机制,如请求解析、连接管理、线程池、类加载器等核心功能的实现。 "可直接导入eclipse"这一特点意味着开发者无需额外的配置步骤,只需简单操作即可在Eclipse这样的集成开发环境中...
### Tomcat7源码手撕过程详解 #### Tomcat初始化流程分析 Tomcat是一个流行的Java Servlet容器,用于部署和运行Web应用程序。理解Tomcat的工作原理对于优化应用性能、解决部署问题至关重要。以下是对Tomcat7启动...
2. "Tomcat的类载入器以及源码分析.doc":详细介绍了Tomcat的类加载器,如WebappClassLoader,它如何根据不同的web应用程序加载类,以及如何避免类冲突。 3. "理解Tomcat的WebappClassLoader(web应用类加载器)一....