本文接下来分析tomcat的类载入器,tomcat需要实现一个自定义的载入器,而不能使用系统类载入器

(1)限制serlvet访问当前运行的java虚拟机中环境变量CLASSPATH指明的路径下的所有类和库,而只允许载入WEB-INF/class目录及其子目录下的类,和从部署的库到WEB-INF/lib目录载入类

(2)提供自动重载的功能,即当WEB-INF/class目录或WEB-INF/lib目录下的类发生变化时,Web应用程序会重新载入这些类

我们先来回顾一下java的类载入器,当我们创建java类的实例时,都必须先将类载入到内存中,java虚拟机使用类载入器来载入需要的类

JVM使用三种类型的类载入器来载入所需要的类,分别为引导类载入器(bootstrap class loader)、扩展类载入器(extensions class loader)和系统类载入器(system class loader)

  • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

JVM采用一种代理模型的类载入机制,可以解决类载入过程中的安全性问题;每当系统需要载入一个类的时候,会首先调用系统类载入器,而系统类载入器将载入类的任务委派给其父载入器,即扩展类载入器,同样扩展类载入器将载入类的任务委派给其父载入器,即引导类载入器;因此引导类载入器会首先执行载入某个类的任务,如果引导类载入器找不到需要载入的类,那么扩展类载入器尝试载入该类,如果扩展类载入器也找不到该类,就轮到系统类载入器继续执行载入任务。如果系统类载入器还是找不到该类,则会抛出java.lang.ClassNotFoundException异常

Tomcat中的载入器是指Web应用程序载入器,而不仅仅指类载入器,载入器必须实现org.apache.catalina.Loader接口

  1. public interface Loader {
  2.  
  3. public ClassLoader getClassLoader();
  4.  
  5. public Container getContainer();
  6.  
  7. public void setContainer(Container container);
  8.  
  9. public DefaultContext getDefaultContext();
  10.  
  11. public void setDefaultContext(DefaultContext defaultContext);
  12.  
  13. public boolean getDelegate();
  14.  
  15. public void setDelegate(boolean delegate);
  16.  
  17. public String getInfo();
  18.  
  19. public boolean getReloadable();
  20.  
  21. public void setReloadable(boolean reloadable);
  22.  
  23. public void addPropertyChangeListener(PropertyChangeListener listener);
  24.  
  25. public void addRepository(String repository);
  26.  
  27. public String[] findRepositories();
  28.  
  29. public boolean modified();
  30.  
  31. public void removePropertyChangeListener(PropertyChangeListener listener);
  32. }

下面我们来具体来分析tomcat容器中 Web应用程序载入器的具体实现,即org.apache.catalina.loader.WebappLoader类实现了上面的Loader接口,负责载入Web应用程序中所使用到的类。

WebappLoader类会创建org.apache.catalina.loader.WebappClassLoader类的实例作为其类载入器;

同时WebappLoader类也实现了org.apache.catalina.Lifecycle接口,可以由其相关联的容器来启动或关闭;

此外,WebappLoader类还实现了java.lang.Runnable接口,通过一个线程不断地调用其类载入器的modified()方法。如果modified()方法返回true, WebappLoader的实例会通知其关联的servlet容器(在这里是Context类的实例),然后由Context实例来完成类的重新载入。

当调用WebappLoader类的start()方法时,会完成以下几项重要工作:

(1)创建一个类载入器

(2)设置仓库

(3)设置类路径

(4)设置访问权限

(5)启动一个新线程来支持自动重载

WebappLoader类会调用其私有方法createClassLoader()方法来创建默认的类载入器

  1. /**
  2. * Create associated classLoader.
  3. */
  4. private WebappClassLoader createClassLoader()
  5. throws Exception {
  6.  
  7. Class clazz = Class.forName(loaderClass);
  8. WebappClassLoader classLoader = null;
  9.  
  10. if (parentClassLoader == null) {
  11. // Will cause a ClassCast is the class does not extend WCL, but
  12. // this is on purpose (the exception will be caught and rethrown)
  13. classLoader = (WebappClassLoader) clazz.newInstance();
  14. } else {
  15. Class[] argTypes = { ClassLoader.class };
  16. Object[] args = { parentClassLoader };
  17. Constructor constr = clazz.getConstructor(argTypes);
  18. classLoader = (WebappClassLoader) constr.newInstance(args);
  19. }
  20.  
  21. return classLoader;
  22.  
  23. }

String loaderClass 的默认值为org.apache.catalina.loader.WebappClassLoader

启动方法后面的设置仓库、设置类路径、设置访问权限等都与类载入器的初始化相关

WebappLoader类支持自动重载功能,如果WEB-INF/class目录或WEB-INF/lib目录下的某些类被重新编译了,那么这些类会自动重新载入,而无需重启tomcat。WebappLoader类使用一个线程周期性地检查每个资源的时间戳,我们可以调用setCheckInterval()方法设置间隔时间

  1. /**
  2. * The background thread that checks for session timeouts and shutdown.
  3. */
  4. public void run() {
  5.  
  6. if (debug >= 1)
  7. log("BACKGROUND THREAD Starting");
  8.  
  9. // Loop until the termination semaphore is set
  10. while (!threadDone) {
  11.  
  12. // Wait for our check interval
  13. threadSleep();
  14.  
  15. if (!started)
  16. break;
  17.  
  18. try {
  19. // Perform our modification check
  20. if (!classLoader.modified())
  21. continue;
  22. } catch (Exception e) {
  23. log(sm.getString("webappLoader.failModifiedCheck"), e);
  24. continue;
  25. }
  26.  
  27. // Handle a need for reloading
  28. notifyContext();
  29. break;
  30.  
  31. }
  32.  
  33. if (debug >= 1)
  34. log("BACKGROUND THREAD Stopping");
  35.  
  36. }

在上面run()方法里面的循环中,首先使线程休眠一段时间,然后调用 WebappLoader实例的类载入器的 modified()方法检查已经载入的类是否被修改,肉没有修改则重新执行循环;否则调用私有方法notifyContext(),通知与WebappLoader实例相关联的Context容器重新载入相关类

  1. /**
  2. * Notify our Context that a reload is appropriate.
  3. */
  4. private void notifyContext() {
  5.  
  6. WebappContextNotifier notifier = new WebappContextNotifier();
  7. (new Thread(notifier)).start();
  8.  
  9. }

上面方法中的WebappContextNotifier为内部类,实现了Runnable接口

  1. /**
  2. * Private thread class to notify our associated Context that we have
  3. * recognized the need for a reload.
  4. */
  5. protected class WebappContextNotifier implements Runnable {
  6.  
  7. /**
  8. * Perform the requested notification.
  9. */
  10. public void run() {
  11. ((Context) container).reload();
  12. }
  13. }

下面我们来分析web应用程序中负责载入类的类载入器org.apache.catalina.loader.WebappClassLoader,该类继承自java.net.URLClassLoader,同时实现了org.apache.catalina.loader.Reloader接口

  1. public interface Reloader {
  2.  
  3. public void addRepository(String repository);
  4.  
  5. public String[] findRepositories();
  6.  
  7. public boolean modified();
  8.  
  9. }

该接口用来与仓库操作相关,同时检查web应用程序中的某个servlet或相关的类是否被修改

每个由WebappClassLoader载入的类,都视为资源,由org.apache.catalina.loader.ResourceEntry类的实例表示,存储了类的相关信息

  1. public class ResourceEntry {
  2.  
  3. public long lastModified = -1;
  4.  
  5. public byte[] binaryContent = null;
  6.  
  7. public Class loadedClass = null;
  8.  
  9. public URL source = null;
  10.  
  11. public URL codeBase = null;
  12.  
  13. public Manifest manifest = null;
  14.  
  15. public Certificate[] certificates = null;
  16.  
  17. }

在WebappClassLoader类中,所有已经缓存的类存储在名为resourceEntries的HashMap类型的变量中,而载入失败的类被存储在另一个名为notFoundResources的HashMap类型的变量中

下面是WebappClassLoader的loadClass()方法的具体实现

  1. public Class loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException {
  3. if (debug >= 2)
  4. log("loadClass(" + name + ", " + resolve + ")");
  5. Class clazz = null;
  6.  
  7. // Don't load classes if class loader is stopped
  8. if (!started) {
  9. log("Lifecycle error : CL stopped");
  10. throw new ClassNotFoundException(name);
  11. }
  12.  
  13. // (0) Check our previously loaded local class cache
  14. clazz = findLoadedClass0(name);
  15. if (clazz != null) {
  16. if (debug >= 3)
  17. log(" Returning class from cache");
  18. if (resolve)
  19. resolveClass(clazz);
  20. return (clazz);
  21. }
  22.  
  23. // (0.1) Check our previously loaded class cache
  24. clazz = findLoadedClass(name);
  25. if (clazz != null) {
  26. if (debug >= 3)
  27. log(" Returning class from cache");
  28. if (resolve)
  29. resolveClass(clazz);
  30. return (clazz);
  31. }
  32.  
  33. // (0.2) Try loading the class with the system class loader, to prevent
  34. // the webapp from overriding J2SE classes
  35. try {
  36. clazz = system.loadClass(name);
  37. if (clazz != null) {
  38. if (resolve)
  39. resolveClass(clazz);
  40. return (clazz);
  41. }
  42. } catch (ClassNotFoundException e) {
  43. // Ignore
  44. }
  45.  
  46. // (0.5) Permission to access this class when using a SecurityManager
  47. if (securityManager != null) {
  48. int i = name.lastIndexOf('.');
  49. if (i >= 0) {
  50. try {
  51. securityManager.checkPackageAccess(name.substring(0,i));
  52. } catch (SecurityException se) {
  53. String error = "Security Violation, attempt to use " +
  54. "Restricted Class: " + name;
  55. System.out.println(error);
  56. se.printStackTrace();
  57. log(error);
  58. throw new ClassNotFoundException(error);
  59. }
  60. }
  61. }
  62.  
  63. boolean delegateLoad = delegate || filter(name);
  64.  
  65. // (1) Delegate to our parent if requested
  66. if (delegateLoad) {
  67. if (debug >= 3)
  68. log(" Delegating to parent classloader");
  69. ClassLoader loader = parent;
  70. if (loader == null)
  71. loader = system;
  72. try {
  73. clazz = loader.loadClass(name);
  74. if (clazz != null) {
  75. if (debug >= 3)
  76. log(" Loading class from parent");
  77. if (resolve)
  78. resolveClass(clazz);
  79. return (clazz);
  80. }
  81. } catch (ClassNotFoundException e) {
  82. ;
  83. }
  84. }
  85.  
  86. // (2) Search local repositories
  87. if (debug >= 3)
  88. log(" Searching local repositories");
  89. try {
  90. clazz = findClass(name);
  91. if (clazz != null) {
  92. if (debug >= 3)
  93. log(" Loading class from local repository");
  94. if (resolve)
  95. resolveClass(clazz);
  96. return (clazz);
  97. }
  98. } catch (ClassNotFoundException e) {
  99. ;
  100. }
  101.  
  102. // (3) Delegate to parent unconditionally
  103. if (!delegateLoad) {
  104. if (debug >= 3)
  105. log(" Delegating to parent classloader");
  106. ClassLoader loader = parent;
  107. if (loader == null)
  108. loader = system;
  109. try {
  110. clazz = loader.loadClass(name);
  111. if (clazz != null) {
  112. if (debug >= 3)
  113. log(" Loading class from parent");
  114. if (resolve)
  115. resolveClass(clazz);
  116. return (clazz);
  117. }
  118. } catch (ClassNotFoundException e) {
  119. ;
  120. }
  121. }
  122.  
  123. // This class was not found
  124. throw new ClassNotFoundException(name);
  125.  
  126. }

在WebappClassLoader实例载入类时,首先是检查缓存,然后再载入指定类(如果设置了安全管理。在代理载入器载入前还要检测类型的安全)

---------------------------------------------------------------------------

本系列How Tomcat Works系本人原创

转载请注明出处 博客园 刺猬的温驯

本人邮箱: chenying998179#163.com (#改为@)

本文链接http://www.cnblogs.com/chenying99/p/3237389.html

How Tomcat Works(十一)的更多相关文章

  1. 攻城狮在路上(肆)How tomcat works(零) 前言说明

    最近几篇是关于How tomcat works一书的读书笔记. 通过数个章节逐渐实现一个tomcat的功能. 源码下载地址:http://zhidao.baidu.com/share/7007af0f ...

  2. How Tomcat works — 四、tomcat启动(3)

    上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看conta ...

  3. How Tomcat Works(十四)补充

    在How Tomcat Works(十四)中,本人并没有对javax.servlet.Filter及javax.servlet.FilterChain做详细的描述,本文在这里做一下补充 FilterC ...

  4. How Tomcat Works(十八)

    在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器.servlet容器.Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来:这种配置应 ...

  5. How Tomcat Works(十七)

    在前面的文章中,已经学会了如何通过实例化一个连接器和容器来获得一个servlet容器,并将连接器和容器相关联:但在前面的文章中只有一个连接器可用,该连接器服务8080端口上的HTTP请求,无法添加另一 ...

  6. How Tomcat Works(十六)

    本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器:本文介绍Host接口和Engine接口及其相关类 Host容器是org.apache.catal ...

  7. How Tomcat Works(十五)

    本文接下来分析Context容器,Context容器实例表示一个具体的Web应用程序,其中包括一个或多个Wrapper实例:不过Context容器还需要其他的组件支持,典型的如载入器和Session管 ...

  8. How Tomcat Works(十四)

    我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...

  9. How Tomcat Works(十三)

    本文分析tomcat容器的安全管理,servlet技术支持通过配置部署描述器(web.xml文件)来对受限内容进行访问控制:servlet容器是通过一个名为验证器的阀来支持安全限制的,当servlet ...

随机推荐

  1. zipline

    history 多只股票时会返回某几只股票停牌没数据 if not symbol(stock) in data: 聚宽 多只股票如果某几只没有发行 600485: nan 多只股票如果某几只停牌 60 ...

  2. mysql mac启动

    设置别名 alias mysql=/usr/local/mysql/bin/mysql alias mysqladmin=/usr/local/mysql/bin/mysqladmin 修改密码 su ...

  3. 使用过的Linux命令

    1 在vim中编辑python,由于tab是8个空格,然而python中是4个,需要替换 :%s/\t/    /g 2 tar tar -czvf topic_dt_poi.0801.0818.ta ...

  4. jacob如何获取word文档的页码

    ActiveXComponent app = new ActiveXComponent("Word.Application"); //启动word String inFile = ...

  5. 小结JS中的OOP(上)

    前言:大家都知道,OOP有三大特性:封装,继承,多态.下面是自己对这三个特性的理解: 封装:把属性与方法整合到某种数据类型中.目的是让类的使用者按类的编写者的意愿去使用类.在封装过程中会一般会做两件事 ...

  6. 解决WebSphere异常:SRVE0199E: 已获取了 OutputStream

    dlg: 例如 在WebSphere这个目录下 /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/temp/master1Node01/master1/gk ...

  7. sqlite3使用简介(内含解决sqlite内存的方法)

    一.使用流程 要使用sqlite,需要从sqlite官网下载到三个文件,分别为sqlite3.lib,sqlite3.dll,sqlite3.h,然后再在自己的工程中配置好头文件和库文件,同时将dll ...

  8. 把一个窗体嵌入到WinForm中进行显示,以CMD窗口为例

    1.添加引用 using System.Runtime.InteropServices; 2. 加入以下代码段 [DllImport("User32.dll ", EntryPoi ...

  9. 九度 Online Judge 之《剑指 Offer》一书相关题目解答

    前段时间准备华为机试,正好之前看了一遍<剑指 Offer>,就在九度 Online Judge 上刷了书中的题目,使用的语言为 C++:只有3题没做,其他的都做了. 正如 Linus To ...

  10. Window nginx+tomcat+https部署方案 支持ios9

    客户端和 Nginx 之间走的 HTTPS 通讯,而 Nginx 到 Tomcat 通过 proxy_pass 走的是普通 HTTP 连接. 下面是详细的配置(Nginx 端口 80/443,Tomc ...