ClassLoader加载项目下的资源文件

Bootstrap、ExtClassLoader、AppClassLoader

首先先要知道三种类加载器的作用。
启动类加载器(Bootstrap
启动类加载器是C++实现的,负责将<JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,出于安全考虑,Bootstrap只加载包名为java、javax、sun等开头的类

扩展类加载器(ExtClassLoader
ExtClassLoaderJava实现的,是sun.misc.Launcher的静态内部类,负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定路径中的类库

系统类加载器(AppClassLoader
AppClassLoader也是Java实现的,同样也是sun.misc.Launcher的静态内部类,负责加载系统类路径java -classpath-D java.class.path指定路径下的类库。我们口中常说的classpath就是AppClassLoader负责加载的。通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器

ClassLoader加载资源

我们启动服务时,IDE会输出当前的Java环境和-classpath-classpath指定了所有JVM要加载的文件和路径,里面一定包含了你当前项目编译后的路径target/classes路径。
在这里插入图片描述
单元测试的-classpath先指定了target/test-classes,然后是target/classes
在这里插入图片描述
这就意味着,使用ClassLoader在单元测试获取资源和正常服务获取资源有差别。

下面是用web服务做的测试

classpath

-classpath只有target/classes路径,加载资源配置文件时,如果不写资源名,只写./、""、/

  • src下的main函数中使用ClassLoaderClassPathResource获取资源文件
    使用ClassLoader#getResource(String name)

    • name=./返回target/classpath/
    • name=""返回在target/classpath/
    • name=/返回null

    使用new ClassPathResource(String path)
    path=/、./、"",都表示target/classes/,对于path=/Spring做了处理

  • 启动的服务,使用ClassLoaderClassPathResource获取资源文件
    对于/、./、""ClassLoaderClassPathResource都表示target/classes/

test-classpath

-classpath先是target/test-classes,然后是target/classes,加载资源配置文件时,如果不写资源名,只写./、""、/

  • 单元测试的main函数中使用ClassLoaderClassPathResource获取资源文件
  • 单元测试方法中调用服务,服务中使用ClassLoaderClassPathResource获取资源文件
  • 单元测试方法中使用ClassLoaderClassPathResource获取资源文件

以上三种,都是优先从target/test-classes加载相应的资源文件,如果没有,再去target/classes下加载资源。
使用ClassLoader时,./和""返回target/test-classes/返回null
使用ClassPathResource时,/、./和""都返回target/test-classes

ClassLoader#getResource(String name)和Class#getResource(String name)的异同

  • ClassLoader#getResource(String name)
    ClassLoader使用双亲委派模型去查找文件资源,先Bootstrap,再ExtClassLoader,最后AppClassLoader,返回的是target/classes或者target/test-classes
public URL getResource(String name) {
    
    
    URL url;
    if (parent != null) {
    
    
        url = parent.getResource(name);
    } else {
    
    
        url = getBootstrapResource(name);
    }
    if (url == null) {
    
    
        url = findResource(name);
    }
    return url;
}
  • Class#getResource(String name)
    源码中先将文件路径定位到当前文件所在的路径下,然后优先获取用户使用的Class中的ClassLoader,如果为null(一般情况下都是AppClassLoader),调用ClassLoader.getSystemResource(name);方法,这个方法中调用了ClassLoader#getSystemClassLoader(),此方法通常返回的是AppClassLoader
public java.net.URL getResource(String name) {
    
    
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
    
    
        // A system class.
        return ClassLoader.getSystemResource(name);
    }
    return cl.getResource(name);
}

private String resolveName(String name) {
    
    
    if (name == null) {
    
    
        return name;
    }
    if (!name.startsWith("/")) {
    
    
        Class<?> c = this;
        while (c.isArray()) {
    
    
            c = c.getComponentType();
        }
        String baseName = c.getName();
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
    
    
            name = baseName.substring(0, index).replace('.', '/')
                +"/"+name;
        }
    } else {
    
    
        name = name.substring(1);
    }
    return name;
}

猜你喜欢

转载自blog.csdn.net/qq_30038111/article/details/111825470