Java에서 리소스를로드하는 기본 방법


107

Java에서 리소스를로드하는 가장 좋은 방법을 알고 싶습니다.

  • this.getClass().getResource() (or getResourceAsStream()),
  • Thread.currentThread().getContextClassLoader().getResource(name),
  • System.class.getResource(name).

답변:


140

원하는대로 솔루션을 찾으십시오 ...

호출되는 클래스에서 getResource/ getResourceAsStream()얻을 두 가지가 있습니다 ...

  1. 클래스 로더
  2. 출발지

그래서 만약 당신이

this.getClass().getResource("foo.txt");

"this"클래스와 동일한 패키지에서 "this"클래스의 클래스 로더를 사용하여 foo.txt를로드하려고 시도합니다. "/"를 앞에 넣으면 절대적으로 리소스를 참조하는 것입니다.

this.getClass().getResource("/x/y/z/foo.txt")

"this"의 클래스 로더와 xyz 패키지에서 리소스를로드합니다 (해당 패키지의 클래스와 동일한 디렉토리에 있어야 함).

Thread.currentThread().getContextClassLoader().getResource(name)

컨텍스트 클래스 로더와 함께로드되지만 패키지에 따라 이름을 확인하지 않습니다 (절대적으로 참조되어야 함).

System.class.getResource(name)

시스템 클래스 로더로 리소스를로드합니다 (java.lang 패키지 (System의 패키지)에 아무것도 넣을 수 없기 때문에 절대적으로 참조되어야합니다.

소스를 살펴보십시오. 또한 getResourceAsStream이 getResource에서 반환 된 URL에서 "openStream"을 호출하고이를 반환 함을 나타냅니다.


AFAIK 패키지 이름은 중요하지 않습니다. 클래스 로더의 클래스 경로입니다.
Bart van Heukelom 2010 년

@Bart 소스 코드를 보면 클래스에서 getResource를 호출 할 때 클래스 이름이 중요하다는 것을 알 수 있습니다. 이 호출이 수행하는 첫 번째 작업은 적절한 경우 패키지 접두사를 추가하는 "resolveName"을 호출하는 것입니다. 된 resolvename에 대한 자바 독은 / "이름은 절대이" "이름이 최고의 절대 제거하지 않은 경우 패키지 이름 접두사를 추가"이다
마이클 와일즈

10
아, 알겠습니다. 여기서 절대는 절대 파일 시스템이 아니라 클래스 경로에 상대적임을 의미합니다.
Bart van Heukelom 2010 년

1
getResourceAsStream ()에서 반환 된 스트림이 null이 아닌지 항상 확인해야한다는 점을 추가하고 싶습니다. 리소스가 클래스 경로 내에없는 경우이기 때문입니다.
stenix

또한 주목할 가치가있는 것은 컨텍스트 클래스 로더를 사용하면 클래스 로더가를 통해 런타임에 변경 될 수 있다는 것 Thread#setContextClassLoader입니다. 프로그램이 실행되는 동안 클래스 경로를 수정해야하는 경우 유용합니다.
Max

14

글쎄, 그것은 부분적으로 당신이 실제로 파생 클래스에 있다면 어떤 일이 일어나기를 원하는지에 달려 있습니다 .

예를 들어, 가정하자가 SuperClass에 a.jar에 있으며 SubClassb.JAR가에, 당신은 인스턴스 메서드에 코드를 실행을하는지에 선언 SuperClass했지만 곳은 this의 인스턴스를 참조 SubClass. 사용 this.getClass().getResource()하면 SubClassB.jar에서에 상대적으로 보입니다. 나는 그것이 일반적으로 필요한 것이 아니라고 생각합니다.

개인적으로 저는 아마도 Foo.class.getResourceAsStream(name)가장 자주 사용할 것입니다. 당신이 쫓고있는 리소스의 이름을 이미 알고 있고 그것이 상대적인 위치를 확신 Foo한다면 그것이 IMO를 수행하는 가장 강력한 방법입니다.

물론 그것이 당신이 원하는 것이 아닐 때도 있습니다. 각 사건의 장점을 판단하십시오. "이 리소스가이 클래스에 번들로 제공된다는 것을 알고 있습니다."가 가장 일반적인 것입니다.


skeet : "SuperClass의 인스턴스 메서드에서 코드를 실행하고 있지만 이것이 SubClass의 인스턴스를 참조하는 경우"라는 문에 의심이 있습니다. 만약 우리가 슈퍼 클래스의 인스턴스 메서드 내에서 명령문을 실행한다면 "this"는 슈퍼 클래스를 참조하지 않습니다. 하위 클래스.
Dead Programmer

1
@Suresh : 아니요. 시도 해봐! 두 개의 클래스를 만들어 하나가 다른 클래스에서 파생되도록 한 다음 수퍼 클래스에서 this.getClass(). 하위 클래스의 인스턴스를 만들고 메서드를 호출합니다. 슈퍼 클래스가 아닌 하위 클래스의 이름이 출력됩니다.
Jon Skeet

감사합니다 하위 클래스 인스턴스 메서드는 슈퍼 클래스의 메서드를 호출합니다.
Dead Programmer

1
내가 궁금해하는 것은 this.getResourceAsStream을 사용하면이 clas가 다른 항아리가 아닌 동일한 항아리에서만 리소스를로드 할 수 있는지 여부입니다. 내 계산에 따르면 리소스를로드하는 것은 클래스 로더이며 확실히 하나의 항아리에서만로드되는 것이 제한되지 않을까요?
Michael Wiles

10

아래와 같이 세 곳을 검색합니다. 의견을 환영합니다.

public URL getResource(String resource){

    URL url ;

    //Try with the Thread Context Loader. 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if(classLoader != null){
        url = classLoader.getResource(resource);
        if(url != null){
            return url;
        }
    }

    //Let's now try with the classloader that loaded this class.
    classLoader = Loader.class.getClassLoader();
    if(classLoader != null){
        url = classLoader.getResource(resource);
        if(url != null){
            return url;
        }
    }

    //Last ditch attempt. Get the resource from the classpath.
    return ClassLoader.getSystemResource(resource);
}

감사합니다. 좋은 생각입니다. 내가 필요한 것만.
devo

2
귀하의 코드에있는 주석을보고 있었는데 마지막 주석이 흥미로워 보입니다. 모든 리소스가 클래스 경로에서로드되지 않습니까? 그리고 ClassLoader.getSystemResource ()는 위의 내용이 성공하지 못한 경우를 어떻게 처리합니까?
nyxz

솔직히 말해서 3 개의 다른 위치에서 파일을로드하려는 이유를 모르겠습니다. 파일이 어디에 저장되어 있는지 모르십니까?
bvdb

3

나는 다른 답변이 정말 늦었다는 것을 알고 있지만 마지막에 도움이 된 것을 공유하고 싶었습니다. 또한 파일 시스템의 절대 경로 (클래스 경로뿐만 아니라)에서 리소스 / 파일을로드합니다.

public class ResourceLoader {

    public static URL getResource(String resource) {
        final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
        classLoaders.add(Thread.currentThread().getContextClassLoader());
        classLoaders.add(ResourceLoader.class.getClassLoader());

        for (ClassLoader classLoader : classLoaders) {
            final URL url = getResourceWith(classLoader, resource);
            if (url != null) {
                return url;
            }
        }

        final URL systemResource = ClassLoader.getSystemResource(resource);
        if (systemResource != null) {
            return systemResource;
        } else {
            try {
                return new File(resource).toURI().toURL();
            } catch (MalformedURLException e) {
                return null;
            }
        }
    }

    private static URL getResourceWith(ClassLoader classLoader, String resource) {
        if (classLoader != null) {
            return classLoader.getResource(resource);
        }
        return null;
    }

}

0

위에서 제안한 많은 방법과 기능을 시도했지만 내 프로젝트에서 작동하지 않았습니다. 어쨌든 나는 해결책을 찾았으며 여기에 있습니다.

try {
    InputStream path = this.getClass().getClassLoader().getResourceAsStream("img/left-hand.png");
    img = ImageIO.read(path);
} catch (IOException e) {
    e.printStackTrace();
}

this.getClass().getResourceAsStream()이 경우 더 잘 사용해야 합니다. getResourceAsStream메서드 의 소스를 살펴보면 여러분과 동일한 일을하지만 더 현명한 방식으로 수행 ClassLoader된다는 것을 알 수 있습니다 (클래스에서 찾을 수 없는 경우 대체 ). 또한 코드에서 잠재력 null을 만날 수 있음을 나타냅니다 getClassLoader
Doc Davluz

@PromCompot, 내가 말했듯 this.getClass().getResourceAsStream()이 나를 위해 작동하지 않으므로 그 작동을 사용합니다. 저와 같은 문제에 직면 할 수있는 사람들이 있다고 생각합니다.
Vladislav
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.