Correct use of Java to obtain resource files

Correct use of Java to obtain resource files

When obtaining files in java, it is often because of not knowing how to write the path, which leads to various problems of failure to obtain files. First classify the common methods of obtaining files:

The first one is to use the IO class File and Path provided by Java to get the file:

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Demo {
    
    
    // 例如 idea下存在resources/test文件
    public static void main(String[] args) {
    
    
        // 使用File或Path进行获取文件时,主要收到相对路径和绝对路径的影响
        String path = "src/main/resources/test";
        File file = new File(path);
        boolean exists = file.exists();
        System.out.println(exists);
        
        
        exists = Files.exists(Paths.get(path));
        System.out.println(exists);
    }
}

When using a relative path to obtain files, System.getProperty("user.dir")the path is used as the starting path, which may cause inconsistencies between the idea and the server runtime.

The second is to use ClassLoader to obtain resource files:

import java.io.File;
import java.io.InputStream;
import java.net.URL;

public class Demo {
    
    
    // 例如 idea下存在resources/test文件
    public static void main(String[] args) {
    
    
        String path = "test";

        // 1 Class获取
        URL classResource = Demo.class.getResource("/test");   // URL
        InputStream classStream = Demo.class.getResourceAsStream("/test"); // InputStream
        File classFile = new File(classResource.getPath());

        // 2 ClassLoader获取
        URL clResource = Demo.class.getClassLoader().getResource("test"); // URL
        InputStream clStream = Demo.class.getClassLoader().getResourceAsStream("test");   // InputStream
        File clFile = new File(clResource.getPath());

    }
}

It should be noted that when using the ClassLoader relative path to obtain, the starting path is the classpath (which is also approximately equal to the resources directory), and when using the class relative path to obtain, the starting directory is the directory of the current class, such as com.zsl0.Demo , Demo.class.getResource("test")when used, is to obtain the com/zsl0/test file, which will cause resources/testthe failure to obtain the file, and the absolute path starts from the classpath.

Therefore, it is recommended to use the method to obtain resource files when obtaining resource files in the resources directory 当前类.class.getClassLoader().getResourceAsStream().

Why does class.getResource()the relative path obtained by the method start from the current class:

public final class Class<T> implements java.io.Serializable,
        GenericDeclaration,
        Type,
        AnnotatedElement {
    
    
    // 省略...
    
    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);
    }
}

Getting the system class loader using the current class's class loader is all the same and Demo.class.getClassLoader().getResource()equivalent ClassLoader.getSystemResource().

public abstract class ClassLoader {
    
    
    public URL getResource(String name) {
    
    
        URL url;
        if (parent != null) {
    
    
            url = parent.getResource(name); // 1. 这里会获取到上级的类加载
        } else {
    
    
            url = getBootstrapResource(name); // 2. 最终会执行到这里
        }
        if (url == null) {
    
    
            url = findResource(name);
        }
        return url;
    }
}

Class loaders who have learned Java should know that the default three class loaders are inheritance relationships (Bootstrap ClassLoader bootstrap class loader, Extension ClassLoader extension class loader, Application ClassLoader application class loader, Custom ClassLoader custom class loader), the above Code parent.getResource(name), which is the source of implementing the parent delegation mechanism.

Through Debug, it is concluded that sun.misc.Launcher$ExtClassLoaderthe resource will be loaded in the end.

Summarize:

  • When getting the local disk path usenew File()
  • When fetching project resource files use 当前类.class.getClassLoader().getResourceAsStream()orClassLoader.getSystemResourceAsStream()

SpringBoot gets resource extension

SpringBoot provides tools ResourceUtils.getURL("classpath:test")to obtain resource files. When carrying classpath:a prefix, use the class loader to obtain it, and otherwise use the URL to obtain it:

public abstract class ResourceUtils {
    
    

    /** Pseudo URL prefix for loading from the class path: "classpath:". */
    public static final String CLASSPATH_URL_PREFIX = "classpath:";
    
    // 省略其它代码...

    public static URL getURL(String resourceLocation) throws FileNotFoundException {
    
    
        Assert.notNull(resourceLocation, "Resource location must not be null");
        if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
    
            // 携带classpath:时进来
            String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
            ClassLoader cl = ClassUtils.getDefaultClassLoader();
            URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));    // 使用类加载获取资源文件
            if (url == null) {
    
    
                String description = "class path resource [" + path + "]";
                throw new FileNotFoundException(description +
                        " cannot be resolved to URL because it does not exist");
            }
            return url;
        }
        try {
    
       // 其它情况
            // try URL
            return new URL(resourceLocation);
        } catch (MalformedURLException ex) {
    
    
            // no URL -> treat as file path
            try {
    
    
                return new File(resourceLocation).toURI().toURL();
            } catch (MalformedURLException ex2) {
    
    
                throw new FileNotFoundException("Resource location [" + resourceLocation +
                        "] is neither a URL not a well-formed file path");
            }
        }
    }
}

It can be seen from the above that classpath:when carrying, the class loader will be used to obtain resources, otherwise the URL (global resource locator, mainly composed of Protocol protocol and Destination destination) will be used to obtain resources.

Guess you like

Origin blog.csdn.net/qq_19152901/article/details/130531716