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);
package org.apache.catalina.loader;
public interface Reloader {
public void addRepository(String repository);
public String[] findRepositories();
public boolean modified();
}
The Reloader接口十分重要的方法就是modified方法,因为假设在web应用程序中一个servlet或者受支持的类已经修改,那么就要返回true. 在实现Reloader接口中的类加载器类中,The addRepository方法是用来添加一个repository(相当一添加一个规则),the findRepositories方法就是返回全部repositories的字符串数组。
二、The WebappLoader 类
The org.apache.catalina.loader.WebappLoader类实现了Loader接口并且代表了一个web应用的loader(主要负责加载web应用程序所需要的类)。WebappLoader类定义了一个成员变量( the org.apache.catalina.loader.WebappClassLoader类实例 )作为它的类加载器。像Catalina其他组件那样,WebappLoader类实现了 org.apache.catalina.Lifecycle,通过与之关联的容器来启动/关闭该组件。The WebappLoader类也实现了 java.lang.Runnable接口,它的目的就是创建一个独立的线程反复的调用class Loader中的modified方法。如果the modified方法返回了true,那么the WebappLoader实例通知与之关联的容器(在本应用程序是一个context)。类的重新加载是由the Context来实现的不是通过WebappLoader类实现。The Context组件到底做了什么请看第十二章的讨论,(StandardContext)。
当The WebappLoader类中的开始方法就会有许多任务要执行如:
1、创建一个class loader
2、设置规则(repositories)
3、设置类路径
4、设置权限
5、开启一个自动部署的线程
每个任务都会在下面的子部分详细讨论。
三、创建一个Class Loader
为了加载一个类,a WebappLoader实例绑定了一个内部的class loader. 你可能会想起前面讨论的Loader接口(仅仅提供了getClassLoader方法,确没有提供setClassLoader)。因此,你无法实例化一个 class loader传给The WebappLoader.The WebappLoader,它并没有和一个默认的class Loader绑定,那么就意味着它就无法绑定class loader了吗?
答案肯定说不,The WebappLoader提供了the getLoaderClasss和setLoaderClass方法目的就是获得和改变私有变量字符串loaderClass。这个变量是一个字符串(代表了the class loader的类名字)。默认情况下,the loaderclass值是org.apache.catalina.loader.WebappClassLoader. 如果你能够创建你自己的class Loader(扩展了WebappClassLoader),就调用setLoaderClass方法迫使你的WebappLoader使用你自己定制的类加载器。否则,当WebappLoader开始启动时,他就会调用自己的私有方法createClassLoader(目的是创建一个WebappClassLoader实例)。下面的方法如下:
private WebappClassLoader createClassLoader()
throws Exception {
Class clazz = Class.forName(loaderClass);
WebappClassLoader classLoader = null;
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;
}
四、设置Repositories
The WebappLoader类中的start方法调用了setRepositories方法(目的的想添加repositories到class loader)。 The WEB-INF/classs 目录当做参数传给了class loader类中的addRepository方法,the WEB-INF/lib目录当做参数传给了class loader类中的setJarPath方法。这种方式,the class loader将能够在WEB-INF/classes目录和一个部署在WEB-INF/lib目录下的任何类库加载classes.
五、 设置类路径
这个任务执行是由the start方法调用了setClassPath方法在servlet context下赋值给了一个属性(该属性字符串包含了Jasper JSP compiler的类路径信息),这里就不在讨论。
六、设置权限
当运行的Tomcat运用了安全管理,那么setPermission方法给class loader添加权限(限制访问目录比如:WEB-INF/classes和WEB-INF/lib). 如果没有安全管理在使用,那么该方法就会立马返回。
七、开始一个自动部署的线程
WebLoader支持自动部署。如果一个类在web-inf.classes或者WEB-INF/lib目录下重新编译了,那么这个类就会在Tomcat不重新启动的前提下,自动的加载这个类。要达到这样的目的,WebappLoader要有一个线程(对每个资源,每个x秒钟)核实时间标记。x 在这儿是被定义的变量checkInterval的值,默认值是15,也就是意味着每个15秒就核实一下。The getCheckInterval和setCheckInterval方法被用来访问这个成员变量。
在Tomcat 4 中WebappLoader实现了java.lang.Runnable接口,目的是支持自动部署。下面是实现Runnable接口的方法:
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");
}
private void notifyContext() {
WebappContextNotifier notifier = new WebappContextNotifier();
(new Thread(notifier)).start();
}
The notifyContext方法没有直接调用The Context接口的reload方法。相反,它是实例化了一个内部类WebappContextNotifier并传递给线程对象然后再调用启动线程的start方法。这种方式,重新加载的功能将会以不同的线程来实现。下面是WebappContextNotifier类代码:
protected class WebappContextNotifier implements Runnable {
/**
* Perform the requested notification.
*/
public void run() {
((Context) container).reload();
}
}
当WebappContextNotifer实例传给了一个线程,那么线程对象中的start方法将会被调用,The WebappContextNotifier实例中的run方法就会被执行。接着,the run方法就会调用Context接口的reload方法。你将会在第十二章看到org.apache.catalina.core.StandardContext类是如何实现the reload方法的。
八、The WebappClassLoader类
The org.apache.catalina.loader.WebappClassLoader类代表了the class loader,负责处理在web应用中的类的加载。WebappClassLoader继承了java.net.URLClassLoader类,这个类我们用来加载java Classes(在前面章节的应用程序中)。
WebappClassLoader优化设计以及进行安全的考虑。比如,它就会缓存前面已经加载好的类,来提高系统性能,它也缓存没有找到的类,如此这样下一次在加载同样的类时,the class loader就会抛出ClassNotFounException异常. WebappClassLoader会搜索存放在repositories下面的类以及具体的JAR文件。关于安全的考虑,WebappClassLoader类就不允许某些类被加载。这些类被存放在一个字符串数组triggers,数组里面有一个值:
private static final String[] triggers={
"javax.servlet.Servlet"
};
同时也不允许加载属于下面的这些包名和子包下,无法一开始就委派到the system class loader中去:
package org.apache.catalina.loader;
import java.net.URL;
import java.security.cert.Certificate;
import java.util.jar.Manifest;
public class ResourceEntry {
public long lastModified = -1;
public byte[] binaryContent = null;
public Class loadedClass = null;
public URL source = null;
public URL codeBase = null;
public Manifest manifest = null;
public Certificate[] certificates = null;
// 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<repositories.length; i++) {
System.out.println(" repository: " + repositories);
}
当你运行该应用程序时,上面几行代码就是显示了docBase和repositories列表。
Resources's 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();
十二、运行应用程序
这里就不必多说
十三、 总结
The Web 应用的Loader或者一个简单的loader在catalina中的组件式尤为重要。一个Loader负责加载类因此要拥有一个内在的class loader.这个内在的class loader是自己定制的类(Tomcat通常在context应用中使用了某些规则),同时定制的class loader支持缓存以及热部署。。。