Java ClassLoader 源码解读

AppClassLoader loadClass

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46); //检查 类的 全路径中是否含有 . 号
    if (var3 != -1) { //含有 . 号
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3)); //检查 类 所在的 包(目录) 是否可以访问
        }
    }

    if (this.ucp.knownToNotExist(var1)) {
        Class var5 = this.findLoadedClass(var1);
        if (var5 != null) {
            if (var2) {
                this.resolveClass(var5);
            }

            return var5;
        } else {
            throw new ClassNotFoundException(var1);
        }
    } else {
        return super.loadClass(var1, var2);//到 ClassLoader 的 loadClass 里面
    }
}

ClassLoader

ClassLoader loadClass

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false); //先动 父 加载器 加载
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) { //没有加载到的话 ,执行 findClass 方法
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

ClassLoader findClass

注意这个方法,实际的实现需要在子类 URLClassLoader 里面查看

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

Launcher getLauncher 过程

注意在 getLauncher 之后的 AppClassLoader 对象之后,AppClassLoader 中的 ucp(urlClassPath )loaders 对象的size 是0
但是在用户程序里面 使用 Thread.currentThread().getContextClassLoader(); 可以看到 ucp.loaders size 已经不是0了,里面是 classpath的东西了

public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader(); //拿到 ExtClassLoader 对象
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); //拿到 AppClassLoader 对象
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

    Thread.currentThread().setContextClassLoader(this.loader);
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
        } else {
            var3 = new SecurityManager();
        }

        if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
        }

        System.setSecurityManager(var3);
    }

}

URLClassLoader

注意 本类的这块代码,在 ExtClassLoader 初始化过程中的父类URLClassLoader会执行这块代码
所以在 AppClassLoader 的 ucp 对象属性中 可以拿到 ExtClassLoader 的 父类URLClassLoader 的 ucp 的值
此时ucp的值就是 [C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext;C:\windows\Sun\Java\lib\ext] 目录中的所有的文件(除了meta-index文件)都放到 URL 对象数组中

static {
        SharedSecrets.setJavaNetAccess(new JavaNetAccess() {
            public URLClassPath getURLClassPath(URLClassLoader var1) {
                return var1.ucp;
            }

            public String getOriginalHostName(InetAddress var1) {
                return var1.holder.getOriginalHostName();
            }
        });
        ClassLoader.registerAsParallelCapable();
    }

URLClassLoader findClass

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");
                    //到 ucp(URLClassPath) 里面查找 resource,(到 java classpath jar中依次查找)
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            return defineClass(name, res);//找到这个 class 的话,到 defineClass 执行(这个方法就是 读取 class 文件,产生 class 对象)  
                        } 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;
}

Launcher.ExtClassLoader 实例话过程

可以看出 ExtClassLoader 集成自 URLClassLoader 类, URLClassLoader 中持有一个 ucp(URLClassPath,用来维护 classpath)

static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;
        //调用这个方法 获取 Launcher.ExtClassLoader 实力
        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {//检查是否已经存在 Launcher.ExtClassLoader 的实例没有
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) { //类锁,防止多次 执行
                    if (instance == null) {
                        instance = createExtClassLoader(); //创建 Launcher.ExtClassLoader 过程
                    }
                }
            }

            return instance; //返回创建好的实例
        }
        
        //创建 Launcher.ExtClassLoader 过程
        private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
            try {
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        File[] var1 = Launcher.ExtClassLoader.getExtDirs(); //获取 配置的 java.ext.dirs (java的ext目录)中的 多个路径 的 File 对象数组
                        //[C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext;C:\windows\Sun\Java\lib\ext]
                        int var2 = var1.length;

                        for(int var3 = 0; var3 < var2; ++var3) {
                            MetaIndex.registerDirectory(var1[var3]); //目前来看没有什么作用
                        }

                        return new Launcher.ExtClassLoader(var1); //使用构造方法 构造 Launcher.ExtClassLoader 对象
                    }
                });
            } catch (PrivilegedActionException var1) {
                throw (IOException)var1.getException();
            }
        }
        
        //增加claspath 路径,使用父类 URLClassLoader 的 方法,最终增加到 父类的类变量 ucp 中(URLClassPath,用来维护 classpath)
        void addExtURL(URL var1) {
            super.addURL(var1);
        }

        public ExtClassLoader(File[] var1) throws IOException {
        //getExtURLs(var1) [C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext;C:\windows\Sun\Java\lib\ext] 目录中的所有的文件(除了meta-index文件)都放到 URL 对象数组中
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }
        
        //获取 配置的 java.ext.dirs (java的ext目录)中的 多个路径 的 File 对象数组 【C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext;C:\windows\Sun\Java\lib\ext】
        private static File[] getExtDirs() {
            String var0 = System.getProperty("java.ext.dirs"); //C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext;C:\windows\Sun\Java\lib\ext
            File[] var1;
            if (var0 != null) {
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens(); //var0以;号分割后的 数量
                var1 = new File[var3]; //创建对应个数的 File 对象数组

                for(int var4 = 0; var4 < var3; ++var4) {
                    var1[var4] = new File(var2.nextToken()); //初始化数组
                }
            } else {
                var1 = new File[0];
            }

            return var1;
        }
        
        //[C:\Program Files\Java\jdk1.8.0_271\jre\lib\ext;C:\windows\Sun\Java\lib\ext] 目录中的所有的文件(除了meta-index文件)都放到 URL 对象数组中
        private static URL[] getExtURLs(File[] var0) throws IOException {
            Vector var1 = new Vector();

            for(int var2 = 0; var2 < var0.length; ++var2) {
                String[] var3 = var0[var2].list();
                if (var3 != null) {
                    for(int var4 = 0; var4 < var3.length; ++var4) {
                        if (!var3[var4].equals("meta-index")) {
                            File var5 = new File(var0[var2], var3[var4]);
                            var1.add(Launcher.getFileURL(var5));
                        }
                    }
                }
            }

            URL[] var6 = new URL[var1.size()];
            var1.copyInto(var6);
            return var6;
        }

        public String findLibrary(String var1) {
            var1 = System.mapLibraryName(var1);
            URL[] var2 = super.getURLs();
            File var3 = null;

            for(int var4 = 0; var4 < var2.length; ++var4) {
                URI var5;
                try {
                    var5 = var2[var4].toURI();
                } catch (URISyntaxException var9) {
                    continue;
                }

                File var6 = var5.getAuthority() == null ? (new File(var5)).getParentFile() : Paths.get(var5).toFile().getParentFile();
                if (var6 != null && !var6.equals(var3)) {
                    String var7 = VM.getSavedProperty("os.arch");
                    File var8;
                    if (var7 != null) {
                        var8 = new File(new File(var6, var7), var1);
                        if (var8.exists()) {
                            return var8.getAbsolutePath();
                        }
                    }

                    var8 = new File(var6, var1);
                    if (var8.exists()) {
                        return var8.getAbsolutePath();
                    }
                }

                var3 = var6;
            }

            return null;
        }

        private static AccessControlContext getContext(File[] var0) throws IOException {
            PathPermissions var1 = new PathPermissions(var0);
            ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
            AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
            return var3;
        }

        static {
            ClassLoader.registerAsParallelCapable();
            instance = null;
        }
    }

AppClassLoader 初始化过程

static class AppClassLoader extends URLClassLoader {
    //此时ucp的值就是 AppClassLoader 父类 URLClassLoader 中的 ucp ,就是  【classpath 中的jar分割开来,组成 File 对象数组 ;再转化到 URL 对象数组】  
    final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

    public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
        final String var1 = System.getProperty("java.class.path");//这个里面的值就是 java 启动的时候 classpath的值,一般包含 JAVA_HOME/lib 和 JAVA_HOME/lib/ext中的jar包  
        final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1); //classpath 中的jar分割开来,组成 File 对象数组  
        return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
            public Launcher.AppClassLoader run() {
                URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2); //classpath 中的jar分割开来,组成 File 对象数组 ;再转化到 URL 对象数组  
                return new Launcher.AppClassLoader(var1x, var0); //使用 AppClassLoader 构造方法 构造 AppClassLoader 对象,这里的var0就是Launcher.ExtClassLoader 实例    
            }
        });
    }
    //var1 就是 classpath 的 URL 对象数组 ;var2 就是 Launcher.ExtClassLoader 实例  
    AppClassLoader(URL[] var1, ClassLoader var2) {
        super(var1, var2, Launcher.factory);
        this.ucp.initLookupCache(this); //这里 ucp(URLClassPath)  中的 lookupCacheURLs 设置为 classpath ;lookupCacheLoader 设置为 this
    }

    // Class.forName 的 入口
    public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
        int var3 = var1.lastIndexOf(46);
        if (var3 != -1) {
            SecurityManager var4 = System.getSecurityManager();
            if (var4 != null) {
                var4.checkPackageAccess(var1.substring(0, var3)); //检查jar包 是否可以访问 
            }
        }

        if (this.ucp.knownToNotExist(var1)) {//使用 ucp(URLClassPath) 看 是否可以 使用自己的 加载器 加载,当然是false了,这里详细的逻辑参考下面的URLClassPath    
            Class var5 = this.findLoadedClass(var1);
            if (var5 != null) {
                if (var2) {
                    this.resolveClass(var5);
                }

                return var5;
            } else {
                throw new ClassNotFoundException(var1);
            }
        } else {//使用父类加载器 加载
        //这个方法在 超级父类 ClassLoader 中,先试用 自己的 parent classloader(Launcher.ExtClassLoader) 加载,  
        //加载不到的话,再使用 父类 URLClassLoader 的 findClass 方法,findClass 是找到 这个 class 在那个jar包
        //findClass 方法里面在找到 这个jar包之后
            return super.loadClass(var1, var2);
        }
    }

    protected PermissionCollection getPermissions(CodeSource var1) {
        PermissionCollection var2 = super.getPermissions(var1);
        var2.add(new RuntimePermission("exitVM"));
        return var2;
    }

    private void appendToClassPathForInstrumentation(String var1) {
        assert Thread.holdsLock(this);

        super.addURL(Launcher.getFileURL(new File(var1)));
    }

    private static AccessControlContext getContext(File[] var0) throws MalformedURLException {
        PathPermissions var1 = new PathPermissions(var0);
        ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
        AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
        return var3;
    }

    static {
        ClassLoader.registerAsParallelCapable();
    }
}

URLClassPath

public URLClassPath(URL[] var1, URLStreamHandlerFactory var2, AccessControlContext var3) {
    this.path = new ArrayList();
    this.urls = new Stack();
    this.loaders = new ArrayList();
    this.lmap = new HashMap();
    this.closed = false;

    for(int var4 = 0; var4 < var1.length; ++var4) {
        this.path.add(var1[var4]);
    }

    this.push(var1); //看这里 把 所以的 依赖jar 都加入到 urls 属性中 
    if (var2 != null) {
        this.jarHandler = var2.createURLStreamHandler("jar");
    }

    if (DISABLE_ACC_CHECKING) {
        this.acc = null;
    } else {
        this.acc = var3;
    }

}

URLClassPath getResource

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

int[] var4 = this.getLookupCache(var1);//这个方法返回为 null

URLClassPath.Loader var3;
for(int var5 = 0; (var3 = this.getNextLoader(var4, var5)) != null; ++var5) {//到getNextLoader 拿到 URLClassPath.Loader  
    Resource var6 = var3.getResource(var1, var2);//依次遍历 classpath jar ,如果里面有 目标的 class 文件,则返回
    if (var6 != null) {
        return var6;
    }
}

return null;
}

URLClassPath getLookupCache

private synchronized int[] getLookupCache(String var1) {
    if (this.lookupCacheURLs != null && lookupCacheEnabled) { //因为 lookupCacheEnabled 是 false,所以这个方法 返回 null
        int[] var2 = getLookupCacheForClassLoader(this.lookupCacheLoader, var1);
        if (var2 != null && var2.length > 0) {
            int var3 = var2[var2.length - 1];
            if (!this.ensureLoaderOpened(var3)) {
                if (DEBUG_LOOKUP_CACHE) {
                    System.out.println("Expanded loaders FAILED " + this.loaders.size() + " for maxindex=" + var3);
                }

                return null;
            }
        }

        return var2;
    } else {
        return null;
    }
}

URLClassPath getNextLoader & getLoader

private synchronized URLClassPath.Loader getNextLoader(int[] var1, int var2) {
    if (this.closed) {
        return null;
    } else if (var1 != null) {//一般 var1 就是 null
        if (var2 < var1.length) {
            URLClassPath.Loader var3 = (URLClassPath.Loader)this.loaders.get(var1[var2]);
            if (DEBUG_LOOKUP_CACHE) {
                System.out.println("HASCACHE: Loading from : " + var1[var2] + " = " + var3.getBaseURL());
            }

            return var3;
        } else {
            return null;
        }
    } else {
        return this.getLoader(var2);//到这个方法来
    }
}

private synchronized URLClassPath.Loader getLoader(int var1) {
    if (this.closed) {
        return null;
    } else {
    //当this.loaders.size() 小于 遍历次数的时候 
        while(this.loaders.size() < var1 + 1) { //this.loaders.size() 一般是 12
            URL var2;
            synchronized(this.urls) {
                if (this.urls.empty()) { //这个 urls,已经在 构造方法里面初始化了
                    return null;
                }

                var2 = (URL)this.urls.pop(); //弹出一个 URL
            }

            String var3 = URLUtil.urlNoFragString(var2);
            if (!this.lmap.containsKey(var3)) {
                URLClassPath.Loader var4;
                try {
                    var4 = this.getLoader(var2); //拿到这个 URL的 URLClassPath.Loader
                    URL[] var5 = var4.getClassPath();
                    if (var5 != null) {
                        this.push(var5); 
                    }
                } catch (IOException var6) {
                    continue;
                } catch (SecurityException var7) {
                    if (DEBUG) {
                        System.err.println("Failed to access " + var2 + ", " + var7);
                    }
                    continue;
                }

                this.validateLookupCache(this.loaders.size(), var3);
                this.loaders.add(var4);
                this.lmap.put(var3, var4);
            }
        }

        if (DEBUG_LOOKUP_CACHE) {
            System.out.println("NOCACHE: Loading from : " + var1);
        }

        return (URLClassPath.Loader)this.loaders.get(var1); //基本上 从 this.loaders 里面依次返回 (已经被jvm初始化过了,size是 classpath的数量)  
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值