java.net.URLClassLoader#findClass解析

​jdk1.8 中 AppClassLoader 与 ExtClassLoader 都继承于 URLClassLoader。
在这里插入图片描述
AppClassLoader 与 ExtClassLoader 没有重写 findClass 方法,URLClassLoader重写了 findClass 方法。
故 findClass 都指向 URLClassLoader。

protected Class<?> findClass(final String name)                                      
    throws ClassNotFoundException                                                    
{                                                                                    
    final Class<?> result;                                                           
    try {                                                                            
        result = AccessController.doPrivileged(                                      
            new PrivilegedExceptionAction<Class<?>>() {                              
                public Class<?> run() throws ClassNotFoundException {                
                    String path = name.replace('.', '/').concat(".class");           
                    Resource res = ucp.getResource(path, false);                     
                    if (res != null) {                                               
                        try {                                                        
                            return defineClass(name, res);                           
                        } catch (IOException e) {                                    
                            throw new ClassNotFoundException(name, e);               
                        }                                                            
                    } else {                                                         
                        return null;                                                 
                    }                                                                
                }                                                                    
            }, acc);                                                                 
    } catch (java.security.PrivilegedActionException pae) {                          
        throw (ClassNotFoundException) pae.getException();                           
    }                                                                                
    if (result == null) {                                                            
        throw new ClassNotFoundException(name);                                      
    }                                                                                
    return result;                                                                   
}                                                                                    
Resource res = ucp.getResource(path, false); 
private final URLClassPath ucp;

http://hg.openjdk.java.net/jdk8/jdk8/jdk/raw-file/687fd7c7986d/src/share/classes/sun/misc/URLClassPath.java

public Resource getResource(String name, boolean check) {
        if (DEBUG) {
            System.err.println("URLClassPath.getResource(\"" + name + "\")");
        }

        Loader loader;
        for (int i = 0; (loader = getLoader(i)) != null; i++) {
            Resource res = loader.getResource(name, check);
            if (res != null) {
                return res;
            }
        }
        return null;
    }
private static class Loader implements Closeable {
        private final URL base;
        private JarFile jarfile;

        Loader(URL var1) { this.base = var1; }
        URL getBaseURL() { return this.base; }
      	// 子类实现
        URL findResource(String var1, boolean var2) {}
        Resource getResource(final String var1, boolean var2) {}
      	// .........
        Resource getResource(String var1) { return this.getResource(var1, true); }
        public void close() throws IOException {
            if (this.jarfile != null) {
                this.jarfile.close();
            }
        }
        URL[] getClassPath() throws IOException {
            return null;
        }
    }

Loader 的实现有两个:
private static class FileLoader extends URLClassPath.Loader {}
static class JarLoader extends URLClassPath.Loader {}

sun.misc.URLClassPath.JarLoader#getResource(java.lang.String, boolean)

@Override                                                
Resource getResource(final String name, boolean check) {
  	// 
    if (metaIndex != null) {                             
        if (!metaIndex.mayContain(name)) {               
            return null;                                 
        }                                                
    }                                                    
                                                         
    try {
      	// 确保 jar 存在
        ensureOpen();                                    
    } catch (IOException e) {                            
        throw new InternalError(e);                      
    }                                                    
    final JarEntry entry = jar.getJarEntry(name);        
    if (entry != null) {                                 
        return checkResource(name, check, entry);        
    }                                                    
                                                         
    if (index == null) {                                 
        return null;                                     
    }                                                    
                                                         
    HashSet<String> visited = new HashSet<String>();     
    return getResource(name, check, visited);            
}                                                                                                               

metaIndex 需要调用 sun.misc.MetaIndex#registerDirectory 注册。
JVM 默认注册的如下:
在这里插入图片描述
JarFile JarEntry的使用:

@Test                                                                                                                        
public void test1() {                                                                                                        
    JarFile jar = null;                                                                                                      
    InputStream inputStream = null;                                                                                          
    try {                                                                                                                    
        jar = new JarFile("/Users/N3verL4nd/.m2/repository/com/sankuai/mms/mms-boot-jetty9/1.3.5/mms-boot-jetty9-1.3.5.jar");
        JarEntry entry = jar.getJarEntry("jetty9.xml");                                                                      
        inputStream = jar.getInputStream(entry);                                                                             
        StreamUtils.copy(inputStream, System.out);                                                                           
    } catch (IOException e) {                                                                                                
        e.printStackTrace();                                                                                                 
    } finally {                                                                                                              
        if (inputStream != null) {                                                                                           
            try {                                                                                                            
                inputStream.close();                                                                                         
            } catch (IOException e) {                                                                                        
                e.printStackTrace();                                                                                         
            }                                                                                                                
        }                                                                                                                    
        if (jar != null) {                                                                                                   
            try {                                                                                                            
                jar.close();                                                                                                 
            } catch (IOException e) {                                                                                        
                e.printStackTrace();                                                                                         
            }                                                                                                                
        }                                                                                                                    
    }                                                                                                                        
}                                                                                                                                                                                                                                                    

getJarEntry 最终的调用为 ZipFile::getEntry

    @Test
    public void test2() throws IOException {
        File file = new File("/Users/n3verl4nd/.m2/repository/com/caucho/hessian/3.1.5/hessian-3.1.5.jar");
        ZipFile zipFile = new ZipFile(file);
        System.out.println("压缩文件的名称为:" + zipFile.getName());
        List<? extends ZipEntry> zipEntries = zipFile.stream().collect(Collectors.toList());
        for (ZipEntry zipEntry : zipEntries) {
            System.out.println(zipEntry);
        }
        System.out.println(zipFile.getEntry("com/caucho/services/message/MessageServiceException.class"));
    }

在这里插入图片描述
sun.misc.URLClassPath.FileLoader#getResource 就是文件夹和文件的拼接。

在这里插入图片描述
openjdk8 对应的源码
在这里插入图片描述

可以看到查找都是相对路径。

参考

https://mp.weixin.qq.com/s/U16h0njbAk6aMC27S_XDZA

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值