How Tomcat Works(十一)
本文接下来分析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接口
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);
}
下面我们来具体来分析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()方法来创建默认的类载入器
/**
* Create associated classLoader.
*/
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; }
String loaderClass 的默认值为org.apache.catalina.loader.WebappClassLoader
启动方法后面的设置仓库、设置类路径、设置访问权限等都与类载入器的初始化相关
WebappLoader类支持自动重载功能,如果WEB-INF/class目录或WEB-INF/lib目录下的某些类被重新编译了,那么这些类会自动重新载入,而无需重启tomcat。WebappLoader类使用一个线程周期性地检查每个资源的时间戳,我们可以调用setCheckInterval()方法设置间隔时间
/**
* The background thread that checks for session timeouts and shutdown.
*/
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"); }
在上面run()方法里面的循环中,首先使线程休眠一段时间,然后调用 WebappLoader实例的类载入器的 modified()方法检查已经载入的类是否被修改,肉没有修改则重新执行循环;否则调用私有方法notifyContext(),通知与WebappLoader实例相关联的Context容器重新载入相关类
/**
* Notify our Context that a reload is appropriate.
*/
private void notifyContext() { WebappContextNotifier notifier = new WebappContextNotifier();
(new Thread(notifier)).start(); }
上面方法中的WebappContextNotifier为内部类,实现了Runnable接口
/**
* Private thread class to notify our associated Context that we have
* recognized the need for a reload.
*/
protected class WebappContextNotifier implements Runnable { /**
* Perform the requested notification.
*/
public void run() {
((Context) container).reload();
}
}
下面我们来分析web应用程序中负责载入类的类载入器org.apache.catalina.loader.WebappClassLoader,该类继承自java.net.URLClassLoader,同时实现了org.apache.catalina.loader.Reloader接口
public interface Reloader { public void addRepository(String repository); public String[] findRepositories(); public boolean modified(); }
该接口用来与仓库操作相关,同时检查web应用程序中的某个servlet或相关的类是否被修改
每个由WebappClassLoader载入的类,都视为资源,由org.apache.catalina.loader.ResourceEntry类的实例表示,存储了类的相关信息
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; }
在WebappClassLoader类中,所有已经缓存的类存储在名为resourceEntries的HashMap类型的变量中,而载入失败的类被存储在另一个名为notFoundResources的HashMap类型的变量中
下面是WebappClassLoader的loadClass()方法的具体实现
public Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
if (debug >= 2)
log("loadClass(" + name + ", " + resolve + ")");
Class clazz = null; // Don't load classes if class loader is stopped
if (!started) {
log("Lifecycle error : CL stopped");
throw new ClassNotFoundException(name);
} // (0) Check our previously loaded local class cache
clazz = findLoadedClass0(name);
if (clazz != null) {
if (debug >= 3)
log(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
} // (0.1) Check our previously loaded class cache
clazz = findLoadedClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
} // (0.2) Try loading the class with the system class loader, to prevent
// the webapp from overriding J2SE classes
try {
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
// Ignore
} // (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = "Security Violation, attempt to use " +
"Restricted Class: " + name;
System.out.println(error);
se.printStackTrace();
log(error);
throw new ClassNotFoundException(error);
}
}
} boolean delegateLoad = delegate || filter(name); // (1) Delegate to our parent if requested
if (delegateLoad) {
if (debug >= 3)
log(" Delegating to parent classloader");
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
} // (2) Search local repositories
if (debug >= 3)
log(" Searching local repositories");
try {
clazz = findClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Loading class from local repository");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
} // (3) Delegate to parent unconditionally
if (!delegateLoad) {
if (debug >= 3)
log(" Delegating to parent classloader");
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
} // This class was not found
throw new ClassNotFoundException(name); }
在WebappClassLoader实例载入类时,首先是检查缓存,然后再载入指定类(如果设置了安全管理。在代理载入器载入前还要检测类型的安全)
---------------------------------------------------------------------------
本系列How Tomcat Works系本人原创
转载请注明出处 博客园 刺猬的温驯
本人邮箱: chenying998179#163.com (#改为@)
本文链接http://www.cnblogs.com/chenying99/p/3237389.html
How Tomcat Works(十一)的更多相关文章
- 攻城狮在路上(肆)How tomcat works(零) 前言说明
最近几篇是关于How tomcat works一书的读书笔记. 通过数个章节逐渐实现一个tomcat的功能. 源码下载地址:http://zhidao.baidu.com/share/7007af0f ...
- How Tomcat works — 四、tomcat启动(3)
上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看conta ...
- How Tomcat Works(十四)补充
在How Tomcat Works(十四)中,本人并没有对javax.servlet.Filter及javax.servlet.FilterChain做详细的描述,本文在这里做一下补充 FilterC ...
- How Tomcat Works(十八)
在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器.servlet容器.Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来:这种配置应 ...
- How Tomcat Works(十七)
在前面的文章中,已经学会了如何通过实例化一个连接器和容器来获得一个servlet容器,并将连接器和容器相关联:但在前面的文章中只有一个连接器可用,该连接器服务8080端口上的HTTP请求,无法添加另一 ...
- How Tomcat Works(十六)
本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器:本文介绍Host接口和Engine接口及其相关类 Host容器是org.apache.catal ...
- How Tomcat Works(十五)
本文接下来分析Context容器,Context容器实例表示一个具体的Web应用程序,其中包括一个或多个Wrapper实例:不过Context容器还需要其他的组件支持,典型的如载入器和Session管 ...
- How Tomcat Works(十四)
我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...
- How Tomcat Works(十三)
本文分析tomcat容器的安全管理,servlet技术支持通过配置部署描述器(web.xml文件)来对受限内容进行访问控制:servlet容器是通过一个名为验证器的阀来支持安全限制的,当servlet ...
随机推荐
- Servlet生命周期以及获取参数
1. 创建Servlet几种方式 1) 实现Servlet接口 控制Servlet的生命周期 构造器 init() service() des ...
- 在页面中使用js
JavaScript:用来在页面编写特效的,和HTML\CSS一样当都是由浏览器解析 JavaScript语言 一.JS如何运行(JavaScript,jscript,VbScript,applet ...
- 今天开始自学Andrew Ng的机器学习,希望可以坚持下来
今天开始正式接触机器学习,20集的课程争取在开学前看完.每看完一集会在博客记录笔记,加油! 版权声明:本文为博主原创文章,未经博主允许不得转载.
- A. Puzzles CodeForces Round #196 (Div.2)
题目的大意是,给你 m 个数字,让你从中选 n 个,使得选出的数字的极差最小. 好吧,超级大水题.因为要极差最小,所以当然想到要排个序咯,然后去连续的 n 个数字,因为数据不大,所以排完序之后直接暴力 ...
- 数组排序,字符串math,replace练习
1. 关于数组排序 排序一直是程序设计中的热门话题.在js中,数组有个sort()方法.在不传参数默认情况下,数组的sort()方法原理是:对数每项调用toString()方法,比较字符串按升 ...
- hql查询语句 内存中的情况,fetch迫切查询关键字
Classes.java package cn.itcast.hiberate.sh.domain; import java.io.Serializable; import java.util.Set ...
- YII Framework学习教程-YII的国际化
一个web应用,发布到互联网,就是面向全球用户.用户在世界的各个角落都可以访问到你的web应用,当然要看你的网站和不和谐,不和谐的web应用在和谐社会是不让你访问的. YII提供了国际化的支持,可以让 ...
- Cent OS5.2安装Hyper-V集成光盘
一.Hyper-V安装windows系统没有问题,windows2000以后系统都可以,一切顺利. 驱动程序:IDE.SCSI.网络.视频和鼠标 要想实现更强的功能,宿主机需要安装Hyper-V集成光 ...
- nginx爆出新漏洞 最低限度可造成DDos攻击
5月9日消息:国内某安全厂商称HTTP代理服务器nginx爆出远程栈缓冲区溢出漏洞,攻击者利用此漏洞可能造成栈溢出,从而执行任意代码,最低限度可造成拒绝服务攻击.目前,官方已经发布安全公告以及相应补丁 ...
- 获取当前的 viewController
- (UIViewController *)currentController { UIViewController *result = nil; UIWindow *window = [ ...