Java的类加载器
具体资料见
首先说明两个术语 仓库(repository),表示类载入器会在哪里搜索要载入的类; 资源(resource),知道一个类加载器中的DirContext对象,它的文件跟路径指的就是上下文的文件跟路径。 在tomcat中,我们使用了自己定义加载器,原因有三: 为了在加载器中指定某些规则; 为了缓存已经加载的类; 为了实现类的预加载;Loader接口
在载入servlet及相关类的时候,须要遵守一些规则。比如,应用程序中的servelt仅仅能引用部署在web-inf/classes文件夹下及其子文件夹下的类,仅仅能訪问web-inf/lib文件夹下的库。
我们在tomcat里说的加载器是指web应用程序加载器,而不不过类加载器。(加载器里面有个类加载器!!!) 加载器应该实现org.apache.catalina.Loader接口,类加载器默觉得WebappClassLoader。package org.apache.catalina;import java.beans.PropertyChangeListener;public interface Loader { public ClassLoader getClassLoader(); public Container getContainer(); //载入器通常与一个context级别的容器相联 public void setContainer(Container container); public DefaultContext getDefaultContext(); public void setDefaultContext(DefaultContext defaultContext); public boolean getDelegate(); //Delegate 代表 托付 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(); //假设容器中的一个或多个类被改动了 modified就会返回true public void removePropertyChangeListener(PropertyChangeListenerlistener);}默认情况下,在context的标准实现---org.apache.catalina.core.StandContext中是不支持自己主动重载的,因此要想开启自己主动重载功能,就须要在server.xml文件里加入一个Context元素,例如以下 <Context path="/myApp" docBase="myApp" debug="0" reloadable="true"/> 在我们这一节的程序中Catalina 提供了 org.apache.catalina.loader.WebappLoader 作为 Load 接口的实现。WebappLoader 对象包括一个org.apache.catalina.loader.WebappClassLoader 类的实例,该类扩展了Java.netURLClassLoader 类。 当与某个加载器相关联的容器须要使用某个servlet时,或者说就是要调用某个servlet的某个方法时,容器首先会调用加载器的getClassLoader()方法返回类加载器,然后再调用类加载器的loadClass()方法来加载这个servlet类。 uml图例如以下
WebAppLoader类
当webapploader类的start方法启动时,会完毕下面几项工作: 1 创建一个类加载器 2 设置仓库 3 设置类路径 4 设置訪问权限 5 启动一个新线程来支持自己主动重载 (在webapploader的run方法中)创建类加载器
private WebappClassLoader createClassLoader() throws Exception { //loadClass为字符串 //默觉得 private String loaderClass ="org.apache.catalina.loader.WebappClassLoader"; //可通过setLoadClass方法更改 Class clazz = Class.forName(loaderClass); WebappClassLoader classLoader = null; //在构造WebAppLoader时 又构造函数的參数指定 if (parentClassLoader == null) { // Will cause a ClassCast is the class does not extend WCL, but // this is on purpose (the exception will be caught and rethrown) classLoader = (WebappClassLoader) clazz.newInstance(); } else { Class [] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoader) constr.newInstance(args); } return classLoader; }当然我们能够通过setLoadClass来改变类载入器的实现,只是 (WebappClassLoader) constr.newInstance(args); 所以,我们自己定义的类也要继承WebappClassLoader。
设置仓库
调用setRepositories,设置WEB-ING/classes文件夹与WEB-INF/lib文件夹设置类路径
和jasper JSP编译器有关设置訪问权限
setPermissions 能够设置类加载器訪问相关路径的权限。比如仅仅能訪问WEB-INf/classes文件夹与WEB-INF/lib文件夹开启新线程运行类的又一次加载
public void run() { if (debug >= 1) log("BACKGROUND THREAD Starting"); // Loop until the termination semaphore is set //整段代码包括在while循环中 //在前面threadDone已经被设置为false //直到程序关闭时,threadDone才会变为true 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"); } private void threadSleep() { //让程序休眠一段时间 时间由checkInterval指定 try { Thread.sleep(checkInterval * 1000L); } catch (InterruptedException e) { ; } }checkInterval,是每隔若干秒来检查一次容器中的类是否有更改! 一旦有更改classLoader.modified()会返回true,直接调用notifyContext();
private void notifyContext() { WebappContextNotifier notifier = new WebappContextNotifier(); (new Thread(notifier)).start(); }protected class WebappContextNotifier implements Runnable { /** * Perform the requested notification. */ public void run() { ((Context) container).reload(); } }WebappContextNotifier是webapploader的内部类。 这里又一次启了一个线程,避免了拥塞。
WebappClassLoader类
该类继承自java.net.URLClassLoader类,后者我们在前面章节已经用过; WebappClassLoader类的设计考虑了安全与优化两个方面。 WebappClassLoader 类不同意一些特定的类被载入。这些类被存储在一个 String 类型的数组中,如今唯独一个成员。 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};
类缓存
为了达到更好的性能,会缓存已经载入的类,这样一来下次在使用这个类的时候,就不用再起载入了。
缓存分两级,一级在本地运行,由webappclassloader实例来管理。 此外,java.lang.ClassLoader类也会维护一个Vector对象,保存已经加载的类,此时缓存由父类管理。 每一个由WebappClassLoader加载的类,都视为资源。是org.apache.catalina.loader.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 中。键值为加载的资源名称。全部找不到的资源都被放在一个名为 notFoundResources 的 HashMap 中。 至于真正的载入类,我们放在下一节说。