Глубокое понимание Java Resource && Spring Resource

Java

существуетJava, чтобы прочитать файл по относительному пути, часто используется следующий метод:

xxx.class.getResource();

xxx.class.getClassLoader().getResource();

существуетSpring, мы также можем пройтиSpringкоторый предоставилResourceСделай что-нибудь:

ClassPathResource

FileSystemResource

ServletContextResource

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");

Вот краткое резюме их различий:


ClassLoader##getResource()

Этот метод является главным действующим лицом на сегодняшний день.

мы все знаемClassLoaderиспользуется для загрузки.classдокумент иClassLoaderэто следоватьJavaМеханизм родительского делегирования при загрузке класса.

Так,ClassLoaderкак ты это нашел.classЧто с файлом? ответURLClassPath

JavaПоставляется с 3ClassLoaderсоответственноBootStrap ClassLoader,EtxClassLoader,AppClassLoader,

эти 3ClassLoaderНаследованиеURLClassLoaderURLClassLoaderсодержитURLClassPathзаписывать каждыйClassLoaderсоответствующая загрузка.classПуть к файлу, когда вам нужно загрузить ресурсы, просто начните сURLClassPathНайдите соответствующий путь.

Вот тестовый код:

System.out.println("BootStrap ClassLoader ");
Stream.of(System.getProperty("sun.boot.class.path").split(";")).forEach(System.out::println);

System.out.println("ExtClassLoader:");  
Stream.of(System.getProperty("java.ext.dirs").split(";")).forEach(System.out::println);

System.out.println("AppClassLoader:");  
Stream.of(System.getProperty("java.class.path").split(";")).forEach(System.out::println);

Результат выглядит следующим образом:

BootStrap ClassLoader 

H:\java\jdk1.8\jre\lib\resources.jar
H:\java\jdk1.8\jre\lib\rt.jar
H:\java\jdk1.8\jre\lib\sunrsasign.jar
H:\java\jdk1.8\jre\lib\jsse.jar
H:\java\jdk1.8\jre\lib\jce.jar
H:\java\jdk1.8\jre\lib\charsets.jar
H:\java\jdk1.8\jre\lib\jfr.jar
H:\java\jdk1.8\jre\classes

ExtClassLoader:

H:\java\jdk1.8\jre\lib\ext
C:\Windows\Sun\Java\lib\ext

AppClassLoader:

H:\java\jdk1.8\jre\lib\charsets.jar
H:\java\jdk1.8\jre\lib\deploy.jar
H:\java\jdk1.8\jre\lib\ext\access-bridge-64.jar
H:\java\jdk1.8\jre\lib\ext\cldrdata.jar
H:\java\jdk1.8\jre\lib\ext\dnsns.jar
H:\java\jdk1.8\jre\lib\ext\jaccess.jar
H:\java\jdk1.8\jre\lib\ext\jfxrt.jar
H:\java\jdk1.8\jre\lib\ext\localedata.jar
H:\java\jdk1.8\jre\lib\ext\nashorn.jar
H:\java\jdk1.8\jre\lib\ext\sunec.jar
H:\java\jdk1.8\jre\lib\ext\sunjce_provider.jar
H:\java\jdk1.8\jre\lib\ext\sunmscapi.jar
H:\java\jdk1.8\jre\lib\ext\sunpkcs11.jar
H:\java\jdk1.8\jre\lib\ext\zipfs.jar
H:\java\jdk1.8\jre\lib\javaws.jar
H:\java\jdk1.8\jre\lib\jce.jar
H:\java\jdk1.8\jre\lib\jfr.jar
H:\java\jdk1.8\jre\lib\jfxswt.jar
H:\java\jdk1.8\jre\lib\jsse.jar
H:\java\jdk1.8\jre\lib\management-agent.jar
H:\java\jdk1.8\jre\lib\plugin.jar
H:\java\jdk1.8\jre\lib\resources.jar
H:\java\jdk1.8\jre\lib\rt.jar
F:\spring-test\target\classes

AppClassLoaderотвечает за часто используемыеJDK jarи от чего зависит проектjarСумка

Вышеуказанные параметры можно получить через sun.misc.Launcher.class

Через выходные параметры мы можем ясно видеть, что каждыйClassLoaderОтветственная зона

Сказав это, это иClassLoader#getResource()Что это значит?

Это большое дело, я только что задал вопрос ранее,ClassLoaderкак читать.classЧто с файлом?

ответURLClassPath#getResource()метод: каждыйUrlClassLoaderна всем протяженииURLClassPathЧтобы сохранить соответствующую область загрузки, когда вам нужно найти.classфайл, проходURLClassPath#getResource()Найди это.


Посмотрим еще разClassLoader#getResource()

//双亲委派查找   
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;
}

//由于BootStrap ClassLoader是C++写的,Java拿不到其引用。
//因此这里单独写了一个方法获取BootStrapResource()
private static URL getBootstrapResource(String name) {
    URLClassPath ucp = getBootstrapClassPath();
    Resource res = ucp.getResource(name);
    return res != null ? res.getURL() : null;
}

URLClassLoader#findResource()

 public URL findResource(final String name) {

        URL url = AccessController.doPrivileged(
            new PrivilegedAction<URL>() {
                public URL run() {
                    return ucp.findResource(name, true);
                }
            }, acc);

        return url != null ? ucp.checkURL(url) : null;
    }

Нам нужно только обратить внимание на это предложениеucp.findResource(name, true);, вот поиск.classфайловый метод, поэтому мы можем сделать выводClassLoader#getResource()процесс:

  • Во-первых,AppClassLoaderделегироватьExtClassLoaderУзнать, есть ли соответствующий ресурс
  • ExtClassLoaderделегироватьBootStrap ClassLoaderНайдите, есть ли соответствующий ресурс
  • BootStrap ClassLoaderпройти черезURLClasspathНайдите область, загруженную самостоятельно, и вернитесь, когда найдете ее.
  • BootStrap ClassLoaderСоответствующий ресурс не найден.ExtClassLoaderпройти черезURLClasspathНайдите область, загруженную самостоятельно, и вернитесь, когда найдете ее.
  • ExtClassLoaderСоответствующий ресурс не найден.AppClassLoaderпройти черезURLClasspathНайдите область, загруженную самостоятельно, и вернитесь, когда найдете ее.
  • AppClassLoaderНе найдено, выдает исключение.

Этот процесс, как раз и загрузка.classФайловый процесс тот же.

Здесь мы можем найти это поClassLoader#getResource()может получитьJDKресурсы, зависит отJARпакеты ресурсов и т. д.

Поэтому мы можем даже написать:

//читатьjava.lang.String.classбайт-код

InputStream inputStream =Test.class.getClassLoader().getResourceAsStream("java/lang/String.class");
try(BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream)){
    byte[] bytes=new byte[1024];
    while (bufferedInputStream.read(bytes)>0){
        System.out.println(new String(bytes, StandardCharsets.UTF_8));
    }
}

ПонялClassLoader#getResource(), На самом деле эта статья почти такая же, потому что нижние слои методов, которые будут использоваться позже, всеClassLoader#getResource()

class##getResource()

class##getResource()дноClassLoader#getResource()

    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);
    }

Но есть небольшая разницаclass#getResource()один дополнительныйresolveName()метод:

    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;
    }

этоresolveName()Примерно определить, является ли путь относительным или абсолютным путем, если это относительный путь, имя ресурса будет добавлено к корневому пути текущего проекта:

Test.class.getResource("spring-config.xml");

становится после разрешения

com/dengchengchao/test/spring-config.xml

Такие ресурсы можно найти только в текущем проекте.


Test.class.getResource("test.txt");    //相对路径

Test.class.getResource("/");           //根路径

Уведомление:ClassLoader#getResource()не может быть/начало


Spring # ClassPathResource()

существуетSpringв, даResourceбыл расширен так, чтоResourceМожет адаптироваться к большему количеству сценариев применения,

ноClssPathResource()дно все ещеClassLoader##getResource(),следовательноClassLoader##getResource()свойства д,ClassPathResourceтакже поддерживает.

    protected URL resolveURL() {
        if (this.clazz != null) {
            return this.clazz.getResource(this.path);
        } else {
            return this.classLoader != null ? this.classLoader.getResource(this.path) : ClassLoader.getSystemResource(this.path);
        }
    }

ClassPathResourceдля чтенияclassesфайл каталога

Вообще говоря, дляSpringBootПроект, структура упакованной проекта выглядит следующим образом:

xxx.jar

|--- BOOT-INF

|--------|--classes

|--------|----|--com

|--------|----|-- application.properties

|--------|----|--logback.xml

| -------|-- lib

|--- META-INF

|--- org

можно увидеть,ClassPathResource()Начальный путьclasses, обычно мы читаемapplication.propertiesэто использоватьClasspathResource()приобретенный

В процессе обычного использования следует обратить внимание на три момента:

  1. Разница между classpath и classpath*:

    Класс: возвращать только первый файл, который выглядит ClassPath *: вернет все файлы нахождения

  2. существуетSpring, вам нужно прямо выразить использованиеClassPathResource()Если вы хотите найти его, вы можете добавить его напрямуюclasspath:голова

  3. использоватьclasspathот/а не к/разницы нет в начале


Spring # ServletContextResource

ServletContextResourceнаправлен наServletделать, мы знаем,ServletРегулированиеwebappКаталог выглядит следующим образом:

image

иServletContextResourceпутьxxxКаталог запускается. ЭтоServletContextResourceполучитьform.htmlи другие ресурсы.

Также сравните вышеClassPathResourceмы можем узнать:

"classpath:com"   

Эквивалентно:

ServletContextResource("WEB-INF/classes/com")

Spring # FileSystemResource

FileSystemResourceНечего сказать, просто ресурсы системного каталога, такие как

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("D://test.xml");

Маркировка головы как этоfile:

Например:

ApplicationContext ctx =
    new FileSystemXmlApplicationContext("flie:D://test.xml");

Если вы считаете, что текст хороший, добро пожаловать в общедоступную учетную запись WeChat: Yiyou Java, время от времени публикуйте несколько статей о продвинутых Java, каждый день, спасибо за внимание.