No Zuo No Die Why You Try

被 URLClassLoader 坑了一下

我真的不喜欢写技术博客,但今天因为一个很小的事情浪费了不少时间,纯当吐个槽吧

就是非常简单的一段有点想当然的代码

import java.net.*;

... ...
    //  /ooo/xxx.jar
    //      - META-INF/
    //          - MANIFEST.MF
    //                Specification-Title: OO & XX
... ...

    ClassLoader myLoader = new URLClassLoader(new URL[]{new URL("jar:file://xxx/ooo.jar!/")});
    
    Class oxClass = Class.forName("foo.xxx.BiliBaLa", true, myLoader); 
    String spec = oxClass.getPackage().getSpecificationTitle();

ClassLoader 的功能一切正常,但是 spec 死活读不出来,怎么回事呢?

估计随便在网上找一个 URLClassLoader 的示例可能也和上面的代码大同小异吧,j2sdk 的文档里面也没怎么提及 URL 应该怎样构造

查了下 SO 好像也没人问过这个问题,没办法,只好跟进去 j2sdk 的源代码看看是啥回事

跟了两下就进入到 sun.misc.URLClassPath,源码居然不在 src.zip 里还真是日了 :doge: 了 还好反编译也不是什么麻烦事,就是没法跟踪代码了,只能靠眼睛看一看猜一猜流程了

刨去虐心的 5000 字之后,定位到这段代码

private URLClassPath.Loader getLoader(final URL var1) throws IOException {
    try {
        return (URLClassPath.Loader) AccessController.doPrivileged(new PrivilegedExceptionAction() {
            public URLClassPath.Loader run() throws IOException {
                String var1x = var1.getFile();
                return (URLClassPath.Loader) (var1x != null && var1x.endsWith("/") 
                        ? ("file".equals(var1.getProtocol())
                            ? new URLClassPath.FileLoader(var1) 
                            : new URLClassPath.Loader(var1))
                        : new URLClassPath.JarLoader(var1, URLClassPath.this.jarHandler, URLClassPath.this.lmap));
            }
        });
    } catch (PrivilegedActionException var3) {
        throw (IOException) var3.getException();
    }
}

要如何才能进到最后那个分支(必须要用 JarLoader 才能确保 Manifest 的加载,分析过程就省去吧)呢?

首先 URL 不能是斜杠结尾,那好我把 "!/ 那个斜杠删掉吧。。。。。。。

java.net.MalformedURLException: no !/ in spec

太。。。天真了




等等

那个 else

好像没有说一定要用 jar: 协议吧

再回去查一下 URLClassLoader 的 javadoc,是这么说的

Any URL that ends with a ‘/’ is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be opened as needed.


Duang!Duang!


呆炸了!!赶快把那个构造改一下呗

ClassLoader myLoader = new URLClassLoader(new URL[]{new URL("file://xxx/ooo.jar")});

居然成功了!

吐血!

太想当然的后果,就是白白浪费了两个小时,我还是默默躲一边儿哭会儿去吧