设为首页 收藏本站
查看: 437|回复: 0

[经验分享] tomcat源码解析--ClassLoader

[复制链接]

尚未签到

发表于 2017-1-27 06:33:40 | 显示全部楼层 |阅读模式
WebappClassLoader,  tomcat 用于加载class 文件的 类
属性, resourceEntries  一个用于缓存web app中classes文件

protected HashMap resourceEntries = new HashMap();
 
属性, notFoundResources 用于存放不存在的请求url, 重复请求,直接处理,不用再次遍历

protected HashMap<String, String> notFoundResources =
new LinkedHashMap<String, String>() {
private static final long serialVersionUID = 1L;
protected boolean removeEldestEntry(
Map.Entry<String, String> eldest) {
return size() > 1000;
}
};
 
添加一个 repository
 

public void addRepository(String repository) {
// Ignore any of the standard repositories, as they are set up using
// either addJar or addRepository
//已经被添加
if (repository.startsWith("/WEB-INF/lib")
|| repository.startsWith("/WEB-INF/classes"))
return;
// Add this repository to our underlying class loader
try {
URL url = new URL(repository);
super.addURL(url);
hasExternalRepositories = true;
repositoryURLs = null;
} catch (MalformedURLException e) {
IllegalArgumentException iae = new IllegalArgumentException
("Invalid repository: " + repository);
iae.initCause(e);
throw iae;
}
}
 另一个 方法 
 
 

//因为 涉及到属性值的覆盖,所以同步
synchronized void addRepository(String repository, File file) {
// Note : There should be only one (of course), but I think we should
// keep this a bit generic
if (repository == null)
return;
if (log.isDebugEnabled())
log.debug("addRepository(" + repository + ")");
int i;
// Add this repository to our internal list
//具体的复制都是通过,复制数组,添加最新,然后重新覆盖来实现
String[] result = new String[repositories.length + 1];
for (i = 0; i < repositories.length; i++) {
result = repositories;
}
result[repositories.length] = repository;
repositories = result;
// Add the file to the list
File[] result2 = new File[files.length + 1];
for (i = 0; i < files.length; i++) {
result2 = files;
}
result2[files.length] = file;
files = result2;
}
 注意: 以上的数组添加参数方法(复制,添加,重置)被大部分开源框架所采用
 
 
modified 方法, 用于判断资源是否被修改, 通过资源的最后修改日期来判读

if (log.isDebugEnabled())
log.debug("modified()");
// Checking for modified loaded resources
int length = paths.length;
// A rare race condition can occur in the updates of the two arrays
// It's totally ok if the latest class added is not checked (it will
// be checked the next time
int length2 = lastModifiedDates.length;
if (length > length2)
length = length2;
for (int i = 0; i < length; i++) {
try {
long lastModified =
((ResourceAttributes) resources.getAttributes(paths))
.getLastModified();
if (lastModified != lastModifiedDates) {
if( log.isDebugEnabled() )
log.debug("  Resource '" + paths
+ "' was modified; Date is now: "
+ new java.util.Date(lastModified) + " Was: "
+ new java.util.Date(lastModifiedDates));
return (true);
}
} catch (NamingException e) {
log.error("    Resource '" + paths + "' is missing");
return (true);
}
}
length = jarNames.length;
// Check if JARs have been added or removed
if (getJarPath() != null) {
try {
NamingEnumeration enumeration = resources.listBindings(getJarPath());
int i = 0;
while (enumeration.hasMoreElements() && (i < length)) {
NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
String name = ncPair.getName();
// Ignore non JARs present in the lib folder
if (!name.endsWith(".jar"))
continue;
if (!name.equals(jarNames)) {
// Missing JAR
log.info("    Additional JARs have been added : '"
+ name + "'");
return (true);
}
i++;
}
if (enumeration.hasMoreElements()) {
while (enumeration.hasMoreElements()) {
NameClassPair ncPair =
(NameClassPair) enumeration.nextElement();
String name = ncPair.getName();
// Additional non-JAR files are allowed
if (name.endsWith(".jar")) {
// There was more JARs
log.info("    Additional JARs have been added");
return (true);
}
}
} else if (i < jarNames.length) {
// There was less JARs
log.info("    Additional JARs have been added");
return (true);
}
} catch (NamingException e) {
if (log.isDebugEnabled())
log.debug("    Failed tracking modifications of '"
+ getJarPath() + "'");
} catch (ClassCastException e) {
log.error("    Failed tracking modifications of '"
+ getJarPath() + "' : " + e.getMessage());
}
}
// No classes have been modified
return (false);
 一个数组 保存 资源路径 paths, 一个数组保存 最后修改日期lastModifiedDates
第二步判断jar 文件资源的更新情况
 
findClass(String) 方法
第一步搜索

if (hasExternalRepositories && searchExternalFirst) {
try {
clazz = super.findClass(name);
 第二步:

if ((clazz == null)) {
try {
clazz = findClassInternal(name);
 都没有,调用父类的

if ((clazz == null) && hasExternalRepositories && !searchExternalFirst) {
try {
clazz = super.findClass(name);
 
  findClassInternal(String)方法
  先找到缓存资源

        ResourceEntry entry = null;
if (securityManager != null) {
PrivilegedAction<ResourceEntry> dp =
new PrivilegedFindResourceByName(name, classPath);
entry = AccessController.doPrivileged(dp);
} else {
entry = findResourceInternal(name, classPath);
}

 findResourceInternal(String,String)  1. 找缓存

  ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
if (entry != null)
return entry;

 2. 在repository中找,如果找到了,就加到paths和lastModifiedDates中
for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
try {
String fullPath = repositories + path;
Object lookupResult = resources.lookup(fullPath);
if (lookupResult instanceof Resource) {
resource = (Resource) lookupResult;
}
// Note : Not getting an exception here means the resource was
// found
entry = findResourceInternal(files, path);
ResourceAttributes attributes =
(ResourceAttributes) resources.getAttributes(fullPath);
contentLength = (int) attributes.getContentLength();
entry.lastModified = attributes.getLastModified();
if (resource != null) {

try {
binaryStream = resource.streamContent();
} catch (IOException e) {
return null;
}
if (needConvert) {
if (path.endsWith(".properties")) {
fileNeedConvert = true;
}
}
// Register the full path for modification checking
// Note: Only syncing on a 'constant' object is needed
synchronized (allPermission) {
int j;
long[] result2 =
new long[lastModifiedDates.length + 1];
for (j = 0; j < lastModifiedDates.length; j++) {
result2[j] = lastModifiedDates[j];
}
result2[lastModifiedDates.length] = entry.lastModified;
lastModifiedDates = result2;
String[] result = new String[paths.length + 1];
for (j = 0; j < paths.length; j++) {
result[j] = paths[j];
}
result[paths.length] = fullPath;
paths = result;
}
}
} catch (NamingException e) {
}
}
 3. 没找到,就找 notFoundResources
if ((entry == null) && (notFoundResources.containsKey(name)))
return null;
 4. 在jar file中寻找
if (!openJARs()) {
return null;
}
for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
//根据path找,如果找到OK,没找到就不用继续了
jarEntry = jarFiles.getJarEntry(path);
if (jarEntry != null) {
entry = new ResourceEntry();
try {
entry.codeBase = getURL(jarRealFiles, false);
String jarFakeUrl = getURI(jarRealFiles).toString();
jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
entry.source = new URL(jarFakeUrl);
entry.lastModified = jarRealFiles.lastModified();
} catch (MalformedURLException e) {
return null;
}
contentLength = (int) jarEntry.getSize();
try {
entry.manifest = jarFiles.getManifest();
binaryStream = jarFiles.getInputStream(jarEntry);
} catch (IOException e) {
return null;
}
// Extract resources contained in JAR to the workdir
if (antiJARLocking && !(path.endsWith(".class"))) {
byte[] buf = new byte[1024];
File resourceFile = new File
(loaderDir, jarEntry.getName());
if (!resourceFile.exists()) {
Enumeration<JarEntry> entries =
jarFiles.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry2 =  entries.nextElement();
if (!(jarEntry2.isDirectory())
&& (!jarEntry2.getName().endsWith
(".class"))) {
resourceFile = new File
(loaderDir, jarEntry2.getName());
try {
if (!resourceFile.getCanonicalPath().startsWith(
canonicalLoaderDir)) {
throw new IllegalArgumentException(
sm.getString("webappClassLoader.illegalJarPath",
jarEntry2.getName()));
}
} catch (IOException ioe) {
throw new IllegalArgumentException(
sm.getString("webappClassLoader.validationErrorJarPath",
jarEntry2.getName()), ioe);
}                                 
resourceFile.getParentFile().mkdirs();
FileOutputStream os = null;
InputStream is = null;
try {
is = jarFiles.getInputStream
(jarEntry2);
os = new FileOutputStream
(resourceFile);
while (true) {
int n = is.read(buf);
if (n <= 0) {
break;
}
os.write(buf, 0, n);
}
} catch (IOException e) {
// Ignore
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
}
}
}
}
}
}
}
 5. 没找到 ,放到notFoundResources中
if (entry == null) {
synchronized (notFoundResources) {
notFoundResources.put(name, name);
}
return null;
}
 6. 最后的处理
if (binaryStream != null) {
byte[] binaryContent = new byte[contentLength];
int pos = 0;
try {
while (true) {
int n = binaryStream.read(binaryContent, pos,
binaryContent.length - pos);
if (n <= 0)
break;
pos += n;
}
} catch (IOException e) {
log.error(sm.getString("webappClassLoader.readError", name), e);
return null;
}
if (fileNeedConvert) {
// Workaround for certain files on platforms that use
// EBCDIC encoding, when they are read through FileInputStream.
// See commit message of rev.303915 for details
// http://svn.apache.org/viewvc?view=revision&revision=303915
String str = new String(binaryContent,0,pos);
try {
binaryContent = str.getBytes("UTF-8");
} catch (Exception e) {
return null;
}
}
entry.binaryContent = binaryContent;
// The certificates are only available after the JarEntry
// associated input stream has been fully read
if (jarEntry != null) {
entry.certificates = jarEntry.getCertificates();
}
}
} finally {
if (binaryStream != null) {
try {
binaryStream.close();
} catch (IOException e) { /* Ignore */}
}
}
   findClassInternal 继续
  2. 如果没找到,直接ClassNotFoundException

if (entry == null)
throw new ClassNotFoundException(name);
 3. 如果已经有了 class 直接返回
Class clazz = entry.loadedClass;
if (clazz != null)
return clazz;
 4. 对于新解析的class ,没有设置 loadedClass参数, 但是必须有binaryContent
if (entry.binaryContent == null)
throw new ClassNotFoundException(name);
 5. 得到class , 先找到包名
// Looking up the package
String packageName = null;
int pos = name.lastIndexOf('.');
if (pos != -1)
packageName = name.substring(0, pos);
Package pkg = null;
if (packageName != null) {
pkg = getPackage(packageName);
// Define the package (if null)
if (pkg == null) {
try {
if (entry.manifest == null) {
definePackage(packageName, null, null, null, null,
null, null, null);
} else {
definePackage(packageName, entry.manifest,
entry.codeBase);
}
} catch (IllegalArgumentException e) {
// Ignore: normal error due to dual definition of package
}
pkg = getPackage(packageName);
}
}
 安全验证
            if (securityManager != null) {
// Checking sealing
if (pkg != null) {
boolean sealCheck = true;
if (pkg.isSealed()) {
sealCheck = pkg.isSealed(entry.codeBase);
} else {
sealCheck = (entry.manifest == null)
|| !isPackageSealed(packageName, entry.manifest);
}
if (!sealCheck)
throw new SecurityException
("Sealing violation loading " + name + " : Package "
+ packageName + " is sealed.");
}
}
 定义class
try {
clazz = defineClass(name, entry.binaryContent, 0,
entry.binaryContent.length,
new CodeSource(entry.codeBase, entry.certificates));
} catch (UnsupportedClassVersionError ucve) {
throw new UnsupportedClassVersionError(
ucve.getLocalizedMessage() + " " +
sm.getString("webappClassLoader.wrongVersion",
name));
}
 这里用到了许多 jdk classLoader下的方法  包括
  definePackage, isPackageSealed,  defineClass 等, 以后自己可以参考
  loadClass(String name, boolean resolve)  加载类 , resolve 默认false
  找到[size=1em]resourceEntries 已经加载过得class,直接返回

clazz = findLoadedClass0(name);
protected Class findLoadedClass0(String name) {
ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
if (entry != null) {
return entry.loadedClass;
}
return (null);  // FIXME - findLoadedResource()
}
  if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return (clazz); }
  使用系统 classLoader 来加载, 避免覆盖

// (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
}
  判断是否设置允许parent classLoader 加载 当 本地repository中没有的时候 delegate属性

if (delegateLoad) {
if (log.isDebugEnabled())
log.debug("  Delegating to parent classloader1 " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}
  判断是否有次class,寻找一次

try {
clazz = findClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Loading class from local repository");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
  都找不到时,无条件的通过parent loader来找(如果自己没有设置的话,也找)

if (!delegateLoad) {
if (log.isDebugEnabled())
log.debug("  Delegating to parent classloader at end: " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug("  Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-333825-1-1.html 上篇帖子: 17,tomcat中StandardContext 下篇帖子: tomcat类加载器之WebappClassLoader
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表