`
dicmo
  • 浏览: 66970 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Coder 爱翻译 How Tomcat Works 第八章 第二部分

    博客分类:
  • j2ee
阅读更多
The Loader Interface

在web应用里加载servlet和加载其它类都是有一定的规则的。例如:应用里的一个servlet可以使用部署在WEB-INF/classes目录及其子目录下的类。但是,servlet不能访问其它类,甚至是包含在JVM运行的Tomcat的CLASSPATH下的类。同样一个servlet也只能访问WEB-INF/lib目录下的类库,不能访问其他目录下的类库。

Tomcat的加载器代表了一个web应用的加载器,而不是一个类加载器。一个加载器必须实现org.apache.catalina.Loader接口。这个加载器实现使用一个
org.apache.catalina.loader.WebappClassLoader类表示的特定类的加载器。你可以在一个web加载器中使用Loader接口的getClassLoader方法来获取一个ClassLoader。

Loader接口定义了与一个仓库(repository)集合一起工作的方法。一个应用的WEB-INF/classes和WEB-INF/lib是目录作为仓库而被添加。Loader接口的addRepository方法用来添加一个仓库,findRepositories方法返回包含了所有仓库的一个数组。

一个Tomcat的加载器实现通常和是一个context相关联的,Loader接口的getContainer和setContainer方法用来在它们之间建立联系。如果在一个context里有一个或多个类被改变,加载器也支持重新加载。这样,servlet程序员编译一个servlet或者一个类,这个新的类在不需要重启Tomcat的情况下会被重新加载。为了实现重新加载,Loader接口有modified方法。在一个加载器实现里,modified方法在仓库中一个或多个类改变了,就返回true。但是一个加载器不会重新加载它自己本身。它调用Context接口的reload方法。其它两个方法:setReloadable和getReloadable用来决定是否使用重新加载这个功能。默认情况,Context的标准实现(org.apache.catalina.core.StandardContext),重新加载是关闭的。要打开context的重新加载功能,你需要在server.xml中添加一个Context元素:
<Context path="/myApp" docBase="myApp" debug="0" reloadable="true"/>

一个加载器实现可以被告知是否使用委托,把它委托给一个父类类加载器。这样,Loader接口提供getDelegate和setDelegate方法。
Listing 8.1: The Loader interface   
 
package org.apache.catalina; 
import java.beans.PropertyChangeListener; 
 
public interface Loader { 
   public ClassLoader getClassLoader(); 
   public Container getContainer(); 
   public void setContainer(Container container); 
   public DefaultContext getDefaultContext(); 
   public void setDefaultContext(DefaultContext defaultContext); 
   public boolean getDelegate(); 
   public void setDelegate(boolean delegate); 
   public String getInfo(); 
   public boolean getReloadable(); 
   public void setReloadable(boolean reloadable); 
   public void addPropertyChangeListener(PropertyChangeListener listener); 
   public void addRepository(String repository); 
   public String[] findRepositories(); 
   public boolean modified(); 
   public void removePropertyChangeListener(PropertyChangeListener listener); 
} 

Catalina提供org.apache.catalina.loader.WebappLoader作为一个Loader接口的实现。WebappLoader对象包含一个org.apache.catalina.loader实例。WebappClassLoader类继承了java.net.URLClassLoader类。
注意:在任何时候,要把容器与一个加载器相关联都需要一个servlet类。当它的invoke方法被调用,容器首先调用加载器的getClassLoader方法来获取类加载器。然后,容器调用类加载器的loadClass方法来加载servlet类。
类图如下:




The Reloader Interface

为了支持自动重新加载,一个类加载器实现必须实现org.apache.catalina.loader.Reloader接口。
Listing 8.2: The Reloader interface   
 
package org.apache.catalina.loader; 
public interface Reloader { 
   public void addRepository(String repository); 
   public String[] findRepositories (); 
   public boolean modified(); 
} 

它最重要的方法是modified。如果在web应用中的一个或多个servlet或类发生变化,它就返回true。它的addRepository方法用来添加一个仓库,findRepositories方法返回在实现了Reloader的类加载器里所有仓库一个字符串数组。

The WebappLoader Class

org.apache.catalina.loader.WebappLoader类是Loader接口的实现,它代表一个负责为web应用加载类的web应用加载器。WebappLoader创建一个org.apache.catalina.loader实例。WebappClassLoader作为它的类加载器。就像其它Catalina组件一样,WebappLoader实现了org.apache.catalina.Lifecycle接口,它被与它相关联的容器启动和停止。WebappLoader类也实现了java.lang.Runnable接口,它可以指定一个线程重复调用它的类加载器的modified方法。如果modified方法返回true,WebappLoader实例通知它的相关容器(本例是一个context)。
通过Context,类重新加载,而不是通过WebappLoader。
当WebappLoader类的start方法被调用时,有几个重要的任务:

 Creating a class loader             创建一个类加载器
 Setting repositories .               设置仓库
 Setting the class path               设置类路径
 Setting permissions                设置权限
 Starting a new thread for auto-reload.  开始一个用于自动重新加载的新线程

下面详细讲解:

Creating A Class Loader

要加载类,一个WebappLoader实例持有一个内部类加载器。你可以回顾Loader接口:这个接口提供getClassLoader方法,但是这里没有setClassLoader方法。此外,你不能实例化一个类加载器和把它传递给WebappLoader。是不是它意味着WebappLoader不能灵活地与一个没有默认的类加载器工作?

答案是否定的。WebappLoader提供getLoaderClass和setLoaderClass方法来获取和改变它的private变量loaderClass的值。这个变量是一个代表了类加载器类名字的字符串。默认情况下,loaderClass的值是:org.apache.catalina.loader.WebappClassLoader。如果你愿意,你可以创建你自己的类加载器,它继承自WebappClassLoader。调用setLoaderClass方法强制WebappLoader使用你的特定的类加载器。当它启动了,WebappLoader将通过调用它的privatecreateClassLoader方法来创建一个WebappClassLoader实例。
Listing 8.3: The createClassLoader method   
 
private WebappClassLoader createClassLoader() throws Exception { 
  Class clazz = Class.forName(loaderClass); 
  WebappClassLoader classLoader = null; 
   if (parentClassLoader == null) { 
     // Will cause a ClassCast if the class does not extend 
     // WebappClassLoader, but this is on purpose (the exception will be 
     // caught and rethrown) 
     classLoader = (WebappClassLoader) clazz.newInstance(); 
     // in Tomcat 5, this if block is replaced by the following: 
     // if (parentClassLoader == null) { 
     //   parentClassLoader = hread.currentThread().getContextClassLoader(); 
     // } 
  else { 
     Class[] argTypes = { ClassLoader.class }; 
     Object[] args = { parentClassLoader }; 
     Constructor constr = clazz.getConstructor(argTypes); 
     classLoader = (WebappClassLoader) constr.newInstance(args); 
   } 
   return classLoader; 
} 

使用WebappClassLoader加载器以外的其它类加载器是可能的。注意:createClassLoader方法返回一个WebappClassLoader。如果你的特定的类加载器没有继承WebappClassLoader的话,这个方法将会抛出一个异常。

Setting Repositories

WebappLoader类的start方法调用setRepositories方法来为它的类加载器添加仓库。WEB-INF/classes目录被传递给类加载器的addRepository方法。WEB-INF/lib目录被传递给类加载器的setJarPath方法。这种方式,类加载器将能够加载在WEB-INF/classes目录下的类和部署在WEB-INF/lib目录下的所有类库。

Setting the Class Path

这个任务是通过start方法,调用setClassPath方法。setClassPath方法指配一个字符串给servlet context的一个属性。这个字符串包含了Jasper JSP编译器的类路径信息。

Setting Permissions

如果在运行Tomcat的时候使用了安全管理器,setPermissions方法添加访问权限给类加载器让它可以访问必要的目录,如:WEB-INF/classes和WEB-INF/lib。如果没有使用安全管理器,这个方法就马上返回。

Starting a New Thread for Auto-Reload

WebappLoader支持自动重新加载。如果在WEB-INF/classes或WEB-INF/lib目录下的类被重编译,在不重启动Tomcat的情况下就可以自动地重新加载这个类。WebappLoader有一个线程每隔X秒时间就检查每一个资源时间戳。这里的X是定义的变量值:checkInterval。默认值是15,表示每隔15秒的时间就为是否需要重新加载做一次时间戳检查。getCheckInterval和setCheckInterval方法用来访问这个变量。

在Tomcat 4的WebappLoader实现了java.lang.Runnable接口来支持重新加载:
Listing 8.3: The run method   
 
public void run() { 
   if (debug >= 1) 
     log("BACKGROUND THREAD Starting"); 
 
   // Loop until the termination semaphore is set 
   while (!threadDone) { 
     // Wait for our check interval 
     threadSleep();
     if (!started) 
       break; 
     try { 
       // Perform our modification check 
       if (!classLoader.modified()) 
         continue; 
     } catch (Exception e) { 
       log(sm.getString("webappLoader.failModifiedCheck"), e); 
       continue; 
     } 
     // Handle a need for reloading 
     notifyContext(); 
     break; 
   } 
if (debug >= 1) 
     log("BACKGROUND THREAD Stopping"); 
} 

注意:Tomcat 5中检查类是否变化这个任务:通过org.apache.catalina.core.StandardContext 对象的backgroundProcess方法完成的。这个方法在每隔一段时间将会被调用。在org.apache.catalina.core.ContainerBase类里的一个特定的线程,ContainerBase是StandardContext的父类。检查ContainerBase类的ContainerBackgroundProcessor内部类实现了Runnable。

它的核心部分,run方法包含一个while循环,它在started变量被设置为false时结束循环:

 通过checkInterval这个变量,等待(sleep)指定的时间间隔。
 任何被加载的类通过调用WebappLoader实例的类加载器的modified方法来检查它们是否被改变。如果没有,continue。
 如果一个类发生了改变,调用notifyContext的private变量告知与WebappLoader相关联的Context来重新加载这个类。

notifyContext方法:
Listing 8.4: The notifyContext method   
 
private void notifyContext() { 
  WebappContextNotifier notifier = new WebappContextNotifier();
   (new Thread(notifier)).start(); 
} 

notifyContext方法不会直接调用Context接口的reload方法。它实例化内部类WebappContextNotifier和传递线程对象,然后调用它的start方法。这种方式,reload的执行将会在另一个线程里面执行。下面是WebappContextNotifier类:
Listing 8.5: The WebappContextNotifier inner class   
 
protected class WebappContextNotifier implements Runnable { 
   public void run() { 
     ((Context) container).reload(); 
   }

当一个WebappContextNotifier实例传递给一个Thread时,这个Thread对象调用它的start方法。WebappContextNotifier实例的run方法将会被执行。run方法调用Context接口的reload方法。


The WebappClassLoader Class

org.apache.catalina.loader.WebappClassLoader类代表负责为web应用加载类的类加载器。WebappClassLoader继承自java.net.URLClassLoader类,这个类用来加载前面几章中应用的java类。
WebappClassLoader的设计是出于优化和安全来考虑的。例如:它缓存先前已经被加载的类来增强性能。它也缓存它没找到的类的名字,以至于下一次同样的类被请求加载时,这个类加载器在不需要先尝试去找到他们就可以抛出ClassNotFoundException异常。WebappClassLoader搜索位于仓库列表下的类和指定JAR文件里面的类。

出于安全性,WebappClassLoader类不允许加载某些类。这些类被存储在一个叫做triggers的字符串数组,通常有一个成员:
private static final String[] triggers = { 
   "javax.servlet.Servlet"                     // Servlet API 
};

也不用首先委托给系统类加载器,不允许你加载属于这些包及其子包下的类:
private static final String[] packageTriggers = { 
   "javax",                                     // Java extensions 
   "org.xml.sax",                                // SAX 1 & 2 
   "org.w3c.dom",                               // DOM 1 & 2 
   "org.apache.xerces",                           // Xerces 1 & 2 
   "org.apache.xalan"                            // Xalan 
};

然我们看看这个类怎么缓存和加载类

Caching

为了获取更好的性能,被加载的类会被缓存起来,以便下次在这个类需要时不需要再次加载。它可以从缓存中取出来。缓存可以在本地处理,缓存是被WebappClassLoader实例管理的。此外,java.lang.ClassLoader维护一个之前被加载过的类的Vector,以防止它这些类被垃圾回收。这种情况,缓存是被它的父类管理的。

每个类会被当做一个资源,它(WEB-INF/classes下或JAR文件里的类文件)是通过WebappClassLoader来加载的。一个资源由org.apache.catalina.loader.ResourceEntry类代表。一个ResourceEntry实例持有类的代表、最后的更改日期、清单(Manifest)等等的字节数组。
The ResourceEntry class is given in Listing 8.6. 
Listing 8.6: The ResourceEntry class.   
 
package org.apache.catalina.loader; 
import java.net.URL; 
import java.security.cert.Certificate;
import java.util.jar.Manifest; 
 
public class ResourceEntry { 
   public long lastModifled = -1; 
   // Binary content of the resource. 
   public byte[] binaryContent = null; 
   public Class loadedClass = null; 
   // URL source from where the object was loaded. 
   public URL source = null; 
   // URL of the codebase from where the object was loaded. 
   public URL CodeBase = null; 
   public Manifest manifest = null; 
   public Certificate[] certificates = null; 
} 

所有的缓存资源被存储在一个叫做resourceEntries的HashMap中。它的key是资源名。所有没有找到的资源被存储在另外一个叫做notFoundResources的HashMap中。

Loading Classes

当加载一个类,WebappClassLoader遵守下面的规则:

 所有之前加载的类被缓存,所有首先会检查本地缓存
 如果没有在本地缓存里找到,就通过调用java.lang.ClassLoader类的findLoadedClass方法检查缓存
 若果两个缓存里都没找到,使用系统的类加载器,防止web应用重写J2EE里的类
 如果使用了安全管理器,检查这个类是否允许加载。如果这个类不允许加载,抛出一个ClassNotFoundException异常。
 如果delegate标识符是on或者如果这个想要被加载类是属于trigger里面包含的包下,使用父类加载器来加载这个类,如果父类加载器是null,使用系统类加载器。
 从当前仓库加载这个类。
 如果在当前仓库里没有发现这个类,如果delegation标识符不是on,使用父类加载器。如果父类加载器是null,使用系统类加载器。
 如果这个类还是没有被发现,就抛出一个ClassNotFoundException异常。

The Application

Context的标准实现是org.apache.catalina.core.StandardContext类。这个应用实例化StandardContext类。StandardContext会在十二章中讨论。你在本章不需要知道关于这个类的详细情况。你只需要知道StandardContext它与一个监听器一起工作。它监听事件的触发。例如:START_EVENT 和STOP_EVENT。监听器必须实现org.apache.catalina.lifecycle.LifecycleListener接口,调用StandardContext类的setConfigured方法。本章的监听器就是由ex08.pyrmont.core.SimpleContextConfig类来表示。
Listing 8.6: The SimpleContextConfig class   
 
package ex08.pyrmont.core; 
import org.apache.catalina.Context; 
import org.apache.catalina.Lifecycle; 
import org.apache.catalina.LifecycleEvent; 
import org.apache.catalina.LifecycleListener; 
 
public class SimpleContextConfig implements LifecycleListener { 
   public void lifecycleEvent(LifecycleEvent event) { 
     if (Lifecycle.START_EVENT.equals(event.getType())) { 
       Context context = (Context) event.getLifecycle(); 
       context.setConfigured(true); 
     } 
   } 
} 

你所需要做的是实例化StandardContext和SimpleContextConfig,然后调用org.apache.catalina.Lifecycle接口的addLifecycleListener方法用StandardContext来注册SimpleContextConfig。

使用StandardContext 的WEB-INF/classes目录下存储的servlet。这个叫做myApp是应用目录在当你第一次部署可下载的ZIP文件时被创建。告诉StandardContext实例在哪里可以找到这个应用目录,你设置一个系统属性:catalina.base 。它的值是user.dir属性。
System.setProperty("catalina.base", System.getProperty("user.dir"));

实际上,这是BootStrap类main方法的第一行代码,然后,main方法实例化默认的连接器。
Connector connector = new HttpConnector();

然后为两个servlet创建各自的wrapper,并实例化它们:
Wrapper wrapper1 = new SimpleWrapper(); 
wrapper1.setName("Primitive"); 
wrapper1.setServletClass("PrimitiveServlet"); 
Wrapper wrapper2 = new SimpleWrapper(); 
wrapper2.setName("Modern"); 
wrapper2.setServletClass("ModernServlet");

然后创建一个StandardContext实例,也设置context的文档库:
Context context = new StandardContext(); 
// StandardContext's start method adds a default mapper 
context.setPath("/myApp"); 
context.setDocBase("myApp");

配置下面的元素在server.xml文件中:
<Context path="/myApp" docBase="myApp"/>

然后把两个wrapper加入到context中,添加映射以便context可以定位wrapper。
context.addChild(wrapper1); 
context.addChild(wrapper2); 
context.addServletMapping("/Primitive", "Primitive"); 
context.addServletMapping("/Modern", "Modern");

下一步是实例化监听器和注册:
LifecycleListener listener = new SimpleContextConfig(); 
((Lifecycle) context).addLifecycleListener(listener);

接下来实例化WebappLoader,然后把它与context相关联起来:

Loader loader = new WebappLoader();
context.setLoader(loader);

最后,把context和默认的连接器相关联起来,调用连接器的initialize和start方法。
connector.setContainer(context); 
  try { 
    connector.initialize(); 
    ((Lifecycle) connector).start(); 
    ((Lifecycle) context).start();

下面几行简单地展示了资源的docBase的值和在类加载器里的所有仓库:
// now we want to know some details about WebappLoader 
WebappClassLoader classLoader = (WebappClassLoader) 
loader.getClassLoader(); 
System.out.println("Resources' docBase: " + 
((ProxyDirContext)classLoader.getResources ()).getDocBase()); 
String[] repositories = classLoader.findRepositories(); 
   for (int i=0; i<repositorles.length; i++) { 
      System.out.println("repository: " + repositories[i]); 
    }

这几行让docBase和仓库的列表当你运行这个应用程序的时候会被打印出来。
Resources’ docBase: C:\HowTomcatWorks\myApp 
repository: /WEB-INF/classes/

docBase的值在你的机器上会有不同的值,依赖于你安装的应用.

最后,直到用户在控制台按了Enter键,就停止应用程序。不然应用就一直处于等待状态。
// make the application wait until we press a key.
    System.in.read(); 
    ((Lifecycle) context).stop();

Running the Application

总结:web应用加载器或一个简单的加载器,是Catalina中最重要的一个组件。一个加载器负责加载类和持有一个内部类加载器。这个内部类加载器是一个特定的类,它被Tomcat用来执行某种规则来加载类。这个特定的类加载器支持缓存和检查单个或多个类是否发生变化。

注:把repository翻译为仓库              
                                                            第八章  完
  • 大小: 33.3 KB
1
2
分享到:
评论

相关推荐

    How tomcat works 中文版

    � jsp / servlet 开发人员,想了解 tomcat 内部机制的 coder ; � 想加入 tomcat 开发团队的 coder ; � web 开发人员,但对软件开发很有兴趣的 coder ; � 想要对 tomcat 进行定制的 coder 。 在阅读之前,希望...

    tomcat原理解析书(how-tomcat-works)中文版

    适合读者 1.jsp/servlet 开发人员,想了解 tomcat 内部机制的 coder;...2.想加入 tomcat 开发团队的 coder; 3.web 开发人员,但对软件开发很有兴趣的 coder; 4.想要对 tomcat 进行定制的 coder。

    Bad Programming Practices 101 Become a Better Coder by Learning How (Not) epub

    Bad Programming Practices 101 Become a Better Coder by Learning How (Not) to Program 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书

    The Clean Coder

    Martin, "The Clean Coder: A Code of Conduct for Professional Programmers" Prentice Hall | 2011 | ISBN: 0137081073 | 256 pages | PDF | 6 MB Programmers who endure and succeed amidst swirling ...

    phpcoder.rar

    phpcoder安装包。 直接下载安装即可。

    HDL-Coder详细教程

    HDL-Coder详细教程,有详细例子,源于官方例程,中文教程

    php coder编辑器

    PHPCoder用于快速开发和调试PHP应用程序,它很容易扩展和定制,完全能够符合开发者的个性要求.PHPCoder是一个非常实用的,功能强大的编程环境,而且它是免费的!

    simulink hdl coder 用户手册pdf

    HDL CODER 的用户手册,学习hdl coder参考用书,详细介绍了用simulink开发fpga的过程

    MediaCoder答题器

    MediaCoder答题器

    Embedded Coder.rar

    texasinstrumentsc2000.mlpkginstall 支持TI的C2000系列工具包,要求MATLAB R2017a及其以上版本。 安装方法:打开matlab,调整路径到mlpkginstall文件所在目录;在current folder窗口里双击mlpkginstall文件即可开始...

    coder的建表语句

    coder的建表语句

    mediacoder专业版

    mediacoder 5685专业版,无普通版的限制

    MediaCoder使用说明文档

    MediaCoder使用说明文档, mediaCoder usermanual,

    range coder.pdf

    range coder, algorithm, compressing.

    MediaCoder.5755专业破解版

    MediaCoder行业版一款针对VOD及KTV视频点播行业开发的,用于转换和处理带有多音轨内容的视频节目的软件。它具备业界领先的视频编码引擎,在高性能转码的同时保持高画质,并通过丰富的视频滤镜增强画面视觉效果。作为...

    matlab Embedded Coder Getting Started Guide.pdf

    Embedded Coder用于产生嵌入式处理器、目标快速原型板和大规模生产中使用的微处理器的可读的、紧凑的、快速的C和C++代码。Embedded Coder支持附加的MATLAB Coder™和Simulink Coder™配置选项,以及对生成代码的功能...

    MediaCoder64位专业破解版

    MediaCoder-Premium-x64 MediaCoder是最早开始使用GPU进行视频编码加速的影音转码软件之一。通过将原本完全依赖CPU的计算工作转移到GPU上进行,H.264和H.265编码所需的时间被大幅缩短。

    AI自动生成SQL语句的开源代码 sqlcoder-main.zip

    开源的AI自动生成SQL语句源代码,这款...SQLCoder2和SQLCoder-7B模型已经向公众开放,大家可以直接拿来嵌入到你的业务开发应用中。大家到这个地址来下载模型:https://huggingface.co/defog/sqlcoder-7b-2,即可使用。

    Mediacoder 使用帮助文档

    Mediacoder 入门使用说明+各种编码常用参数设置。

Global site tag (gtag.js) - Google Analytics