Java类加载双亲委派模型
Java类加载器是用户程序和JVM虚拟机之间的桥梁,在Java程序中起了至关重要的作用。java中的类加载默认是采用双亲委派模型,即加载一个类时,首先判断自身define加载器有没有加载过此类,如果加载了直接获取class对象,如果没有查到,则交给加载器的父类加载器去重复上面过程。
Loader接口
在载入Web应用程序中需要的servlet类及其相关类时要遵守一些明确的规则,例如应用程序中的servlet只能引用部署在WEB-INF/classes目录及其子目录下的类。但是,servlet类不能访问其它路径中的类,即使这些累包含在运行当前Tomcat的JVM的CLASSPATH环境变量中。此外,servlet类只能访问WEB-INF/LIB目录下的库,其它目录的类库均不能访问。Tomcat中的载入器值得是Web应用程序载入器,而不仅仅是类载入器,载入器必须实现Loader接口。Loader接口的定义如下所示:
public interface Loader {
public void backgroundProcess();
public ClassLoader getClassLoader();
public Context getContext();
public void setContext(Context context);
public boolean getDelegate();
public void setDelegate(boolean delegate);
public void addPropertyChangeListener(PropertyChangeListener listener);
public boolean modified();
public void removePropertyChangeListener(PropertyChangeListener listener);
}
后台任务:Loader接口需要进行在servlet类变更的时候实现类的重新加载,这个任务就是在backgroundProcess()
中实现的,WebApploader中backgroundProcess()
的实现如下所示。可以看到,当Context容器开启了Reload功能并且仓库变更的情况下,Loaders会先把类加载器设置为Web类加载器,重启Context容器。重启Context容器会重启所有的子Wrapper容器,会销毁并重新创建servlet类的实例,从而达到动态加载servlet类的目的。
@Override
public void backgroundProcess() {
Context context = getContext();
if (context != null) {
if (context.getReloadable() && modified()) {
ClassLoader originalTccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(WebappLoader.class.getClassLoader());
context.reload();
} finally {
Thread.currentThread().setContextClassLoader(originalTccl);
}
}
}
}
类加载器:Loader的实现中,会使用一个自定义类载入器,它是WebappClassLoader类的一个实例。可以使用Loader接口的getClassLoader()方法来获取Web载入器中的ClassLoader的实例。默认的类加载器的实现有两种种:ParallelWebappClassLoader和WebappClassLoader
Context容器:Tomcat的载入器通常会与一个Context级别的servelt容器相关联,Loader接口的getContainer()方法和setContainer()方法用来将载入器和某个servlet容器关联。如果Context容器中的一个或者多个类被修改了,载入器也可以支持对类的重载。这样,servlet程序员就可以重新编译servlet类及其相关类,并将其重新载入而不需要重新启动Tomcat。Loader接口使用modified()方法来支持类的自动重载。
类修改检测:在载入器的具体实现中,如果仓库中的一个或者多个类被修改了,那么modified()方法必须放回true,才能提供自动重载的支持
父载入器:载入器的实现会指明是否要委托给父类的载入器,可以通过setDelegate()和getDelegate方法配置。
WebappLoader类
Tomcat中唯一实现Loader接口的类就是WebappLoader类,其实例会用作Web应用容器的载入器,负责载入Web应用程序中所使用的类。在容器启动的时候,WebApploader会执行以下工作:
- 创建类加载器
- 设置仓库
- 设置类的路径
- 设置访问权限
- 启动新线程来支持自动重载
创建类加载器
为了完成类加载功能,WebappLoader会按照配置创建类加载器的实例,Tomcat默认有两种类加载器:WebappClassLoader和ParallelWebappClassLoader,默认情况下使用ParallelWebappClassLoader作为类加载器。用户可以通过setLoaderClass()设置类加载器的名称。WebappLoader创建类加载器的源码如下所示,我们可以看到类加载器的实例必须是WebappClassLoaderBase的子类。
private WebappClassLoaderBase createClassLoader()