写点什么

原来 user.dir 竟然会影响 classpath 的值

作者:Java你猿哥
  • 2023-06-03
    湖南
  • 本文字数:1575 字

    阅读完需:约 5 分钟

起因:使用脚本启动,脚本设置了 classpath 的路径为当前路径。但是从 tomcat 日志打印来看,扫描的不仅仅是当前路径,而是主目录下所有的文件夹都被扫描。导致项目启动异常缓慢。classpath 设置如下:

set CLASSPATH=.;
复制代码

当项目启动,查看 APPClassLoader 的 url。按理来说“.”代表当前目录,也就是脚本执行的目录,是在 bin 目录下面。但是 APPClassLoader 的 url 却是主目录。我百思不得其解,只能翻 JDK 的源码看看 AppClassLoader 怎么加载它的 URL。那么下面就跟我一起带着问题来看看答案吧。


以 JDK 8 为例,AppClassLoader 的源码位于 sun.misc.Launcher#AppClassLoader。

static class AppClassLoader extends URLClassLoader {
static { ClassLoader.registerAsParallelCapable(); }
public static ClassLoader getAppClassLoader(final ClassLoader extcl) throws IOException { //在脚本中设置的值,在这个方法中可以拿到。此时s="."; final String s = System.getProperty("java.class.path"); final File[] path = (s == null) ? new File[0] : getClassPath(s);
return AccessController.doPrivileged( new PrivilegedAction<AppClassLoader>() { public AppClassLoader run() { //设置url的关键代码,也就是此时的重点关注对象 URL[] urls = (s == null) ? new URL[0] : pathToURLs(path); return new AppClassLoader(urls, extcl); } }); }
复制代码

从上方的代码可以看到 pathToURLs(path)是把"."转化为路径的关键代码。那么接下看看这个方法干了啥吧。

private static URL[] pathToURLs(File[] path) {    URL[] urls = new URL[path.length];    for (int i = 0; i < path.length; i++) {       //好吧,关键代码又来到了getFileURL这个方法        urls[i] = getFileURL(path[i]);    }    return urls;}
复制代码


static URL getFileURL(File file) {        //在这个方法中路径“.”,发生了变化。        file = file.getCanonicalFile();       return ParseUtil.fileToEncodedURL(file);}
复制代码

代码一层一层的绕,为了方便阅读,我搞点伪代码。下方是伪代码示例:

public File getCanonicalFile() throws IOException {      //好吧url的处理来到getCanonicalPath方法    String canonPath = getCanonicalPath();    return new File(canonPath, fs.prefixLength(canonPath));}
复制代码


public String getCanonicalPath() throws IOException {      //fs.resolve(this)这是重点关注对象    return fs.canonicalize(fs.resolve(this));}
复制代码

现在从 fs.resolve(this)看看 url 的变化

public String resolve(File f) {    //此时这个path的值是“.”    String path = f.getPath();    //pl的结果是0,那就跳到对应的if去吧    int pl = f.getPrefixLength();    if ((pl == 2) && (path.charAt(0) == slash))        return path;                        /* UNC */    if (pl == 3)        return path;                        /* Absolute local */        //转化为路径的关键代码出现了,getUserPath()    if (pl == 0)        return getUserPath() + slashify(path); /* Completely relative */}
复制代码

来看看上方 getUserPath()的实现吧

private String getUserPath() {    return normalize(System.getProperty("user.dir"));}
复制代码

意思就是如果我们的 classpath=.;那么如果我们在环境变量设置 user.dir 的值。classpath 的值就变成了 user.dir 的值。很不巧我们的启动脚本找到关于 user.dir 的赋值:set -Duser.dir="主目录"。这就解释了为什么 classpath 设置了“.”而 APPClassLoader 的 url 却变成了主目录。

用户头像

Java你猿哥

关注

一只在编程路上渐行渐远的程序猿 2023-03-09 加入

关注我,了解更多Java、架构、Spring等知识

评论

发布
暂无评论
原来user.dir竟然会影响classpath的值_Java_Java你猿哥_InfoQ写作社区