您的位置:首页 > 运维架构 > Tomcat

Tomcat的WebappClassLoader(web应用类加载器)详解(一)

2013-05-06 16:31 387 查看
Tomcat负责Web应用的类加载的是org.apache.catalina.loader.WebappClassLoader,它有几个比较重要的方法:findClass(),loadClass(),findClassInternal(),findResourceInternal().

WebappClassLoader类加载器被用来加载一个类的时候,loadClass()会被调用,loadClass()则调用findClass()。后两个方法是WebappClassLoader的私有方法,findClass()调用findClassInternal()来创建class对象,而findClassInternal()则需要findResourceInternal()来查找.class文件。

通常自己实现类记载器只要实现findclass即可,这里为了实现特殊目的而override了loadClass().

下面是精简过的代码(去除了几乎全部关于log、异常和安全控制的代码):

loadClass:

publicClass
loadClass(Stringname,
boolean resolve)

throwsClassNotFoundException{

Classclazz
= null;

//(0) 先从自己的缓存中查找,有则返回,无则继续

clazz= findLoadedClass0(name);

if(clazz
!=null)
{

if(resolve)
resolveClass(clazz);

return(clazz);

}

//(0.1) 再从parent的缓存中查找

clazz=
findLoadedClass(name);

if(clazz
!=null)
{

if(resolve)
resolveClass(clazz);

return(clazz);

}

//(0.2) 缓存中没有,则首先使用system类加载器来加载

clazz
=system.loadClass(name);

if
(clazz !=null)
{

if(resolve)
resolveClass(clazz);

return(clazz);

}

//判断是否需要先让parent代理

booleandelegateLoad
= delegate
||filter(name);

//(1) 先让parent加载,通常delegateLoad ==false,即这一步不会执行

if(delegateLoad)
{

ClassLoaderloader
= parent;

if(loader
==null)

loader=
system;

clazz= loader.loadClass(name);

if(clazz
!=null)
{

if(resolve)
resolveClass(clazz);

return(clazz);

}

}

//(2) delegateLoad == false 或者 parent加载失败,调用自身的加载机制

clazz=
findClass(name);

if(clazz
!=null)
{

if(resolve)
resolveClass(clazz);

return(clazz);

}

//(3) 自己加载失败,则请求parent代理加载

if(!delegateLoad)
{

ClassLoaderloader
= parent;

if(loader
==null)

loader=
system;

clazz= loader.loadClass(name);

if(clazz
!=null)
{

return(clazz);

}

}

thrownew
ClassNotFoundException(name);

}

findClass:

public
Class findClass(Stringname)
throws ClassNotFoundException
{

//先试图自己加载类,找不到则请求parent来加载

//注意这点和java默认的双亲委托模式不同

Classclazz
= null;

clazz= findClassInternal(name);

if((clazz
==null)
&&hasExternalRepositories){

synchronized(this){

clazz=
super.findClass(name);

}

}

if(clazz
==null)
{

thrownew
ClassNotFoundException(name);

}

return(clazz);

}

findClassInternal:

protected
Class findClassInternal(Stringname)

throwsClassNotFoundException{

if(!validate(name))

thrownew
ClassNotFoundException(name);

//根据类名查找资源

StringtempPath
= name.replace('.','/');

StringclassPath
= tempPath+
".class";

ResourceEntryentry
= null;

entry=findResourceInternal(name,classPath);

if(entry
==null)

thrownew
ClassNotFoundException(name);

//如果以前已经加载成功过这个类,直接返回

Classclazz
= entry.loadedClass;

if(clazz
!=null)

returnclazz;

//以下根据找到的资源(.class文件)进行:1、定义package;2、对package安全检查;3、定义class,即创建class对象

synchronized(this){

if(entry.binaryContent
==null
&& entry.loadedClass
==null)

thrownew
ClassNotFoundException(name);

//Looking up the package

StringpackageName
= null;

intpos
= name.lastIndexOf('.');

if(pos
!=-1)

packageName=
name.substring(0,pos);

Packagepkg
= null;

if(packageName
!=null)
{

pkg=
getPackage(packageName);

//Define the package (if null)

if(pkg
==null)
{

//定义package的操作,此处省略,具体参看源码

pkg=
getPackage(packageName);

}

}

if(securityManager
!=null)
{

//安全检查操作,此处省略,具体参看源码

}

//创建class对象并返回

if(entry.loadedClass
==null)
{

try{

clazz=
defineClass(name,entry.binaryContent, 0,

entry.binaryContent.length,

newCodeSource(entry.codeBase,
entry.certificates));

}catch
(UnsupportedClassVersionErrorucve)
{

thrownew
UnsupportedClassVersionError(

ucve.getLocalizedMessage()+
" " +

sm.getString("webappClassLoader.wrongVersion",

name));

}

entry.loadedClass= clazz;

entry.binaryContent=
null;

entry.source=
null;

entry.codeBase=
null;

entry.manifest=
null;

entry.certificates=
null;

}else
{

clazz= entry.loadedClass;

}

}

returnclazz;

}

findResouceInternal():

//要先加载相关实体资源(.jar) 再加载查找的资源本身

protected ResourceEntry findResourceInternal(String name, Stringpath) {

//先根据类名从缓存中查找对应资源 ,有则直接返回

ResourceEntry entry = (ResourceEntry)resourceEntries.get(name);

if (entry != null) return entry; int contentLength = -1;

//资源二进制数据长度

InputStream binaryStream = null;

//资源二进制输入流

int jarFilesLength = jarFiles.length;

//classpath中的jar包个数

int repositoriesLength = repositories.length;

//仓库数(classpath每一段称为repository仓库)

int i; Resource resource = null;

//加载的资源实体

boolean fileNeedConvert = false;

//对每个仓库迭代,直接找到相应的entry,如果查找的资源是一个独立的文件,在这个代码块可以查找到相应资源,

//如果是包含在jar包中的类,这段代码并不能找出其对应的资源

for (i = 0; (entry == null) &&(i < repositoriesLength); i++) {

try {

String fullPath = repositories[i] + path;

//仓库路径 加资源路径得到全路径

Object lookupResult = resources.lookup(fullPath);

//从资源库中查找资源

if (lookupResult instanceof Resource) {

resource = (Resource) lookupResult;

}

//到这里没有抛出异常,说明资源已经找到,现在构造entry对象

if (securityManager != null) {

PrivilegedAction dp = new PrivilegedFindResource(files[i],path);

entry = (ResourceEntry)AccessController.doPrivileged(dp);

} else {

entry = findResourceInternal(files[i], path);

//这个方式只是构造entry并给其codebase和source赋值

}

//获取资源长度和最后修改时间

ResourceAttributesattributes = (ResourceAttributes)resources.getAttributes(fullPath);

contentLength = (int) attributes.getContentLength();

entry.lastModified= attributes.getLastModified();

//资源找到,将二进制输入流赋给binaryStream

if (resource != null) {

try {

binaryStream = resource.streamContent();

} catch (IOException e) {

return null;

}

//将资源的最后修改时间加到列表中去,代码略去,参加源码

}

} catch (NamingException e) { }

}

if ((entry == null) &&(notFoundResources.containsKey(name)))

return null;

//开始从jar包中查找

JarEntry jarEntry =null;

synchronized (jarFiles) {

if (!openJARs()) { return null; }

for(i = 0; (entry == null) && (i< jarFilesLength); i++) {

jarEntry = jarFiles[i].getJarEntry(path);

//根据路径从jar包中查找资源

//如果jar包中找到资源,则定义entry并将二进制流等数据赋给entry,同时将jar包解压到workdir.

if(jarEntry != null) {

entry = new ResourceEntry();

try {

entry.codeBase = getURL(jarRealFiles[i], false);

StringjarFakeUrl = getURI(jarRealFiles[i]).toString();

jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;

entry.source= new URL(jarFakeUrl);

entry.lastModified= jarRealFiles[i].lastModified();

} catch (MalformedURLException e) {

return null;

}

contentLength = (int) jarEntry.getSize();

try {

entry.manifest = jarFiles[i].getManifest();

binaryStream= jarFiles[i].getInputStream(jarEntry);

}catch (IOException e) { return null; }

if (antiJARLocking &&!(path.endsWith(".class"))) {

//解压jar包代码,参见源码 }

}

}

if (entry == null) { synchronized (notFoundResources) {notFoundResources.put(name, name); } return null; } //从二进制流将资源内容读出if (binaryStream != null) { byte[] binaryContent = newbyte[contentLength]; //读二进制流的代码,这里省去,参见源码 entry.binaryContent
=binaryContent; } } // 将资源加到缓存中 synchronized (resourceEntries) {ResourceEntry entry2 = (ResourceEntry) resourceEntries.get(name);if (entry2 == null) { resourceEntries.put(name, entry); } else {entry = entry2; } } return entry; }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: