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[i] = repositories[i]; } 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[i] = files[i]; } 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[i])) .getLastModified(); if (lastModified != lastModifiedDates[i]) { if( log.isDebugEnabled() ) log.debug(" Resource '" + paths[i] + "' was modified; Date is now: " + new java.util.Date(lastModified) + " Was: " + new java.util.Date(lastModifiedDates[i])); return (true); } } catch (NamingException e) { log.error(" Resource '" + paths[i] + "' 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[i])) { // 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[i] + 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[i], 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[i].getJarEntry(path); if (jarEntry != null) { entry = new ResourceEntry(); try { entry.codeBase = getURL(jarRealFiles[i], false); String jarFakeUrl = 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; } // 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[i].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[i].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
找到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) { ; } }