tomcat源码解析--ClassLoader

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) {
                ;
            }
        }

猜你喜欢

转载自patrick002.iteye.com/blog/2146158