Jaka jest różnica między Class.getResource () a ClassLoader.getResource ()?

194

Zastanawiam się, jaka jest różnica między Class.getResource()i ClassLoader.getResource()?

edycja: Chcę szczególnie wiedzieć, czy buforowanie jest zaangażowane na poziomie pliku / katalogu. Jak w „czy katalogi są buforowane w wersji Class?”

AFAIK następujące czynności powinny zasadniczo zrobić to samo, ale nie są to:

getClass().getResource() 
getClass().getClassLoader().getResource()

Odkryłem to, majstrując przy kodzie generującym raporty, który tworzy nowy plik WEB-INF/classes/z istniejącego pliku w tym katalogu. Korzystając z metody z klasy, mogłem znaleźć pliki, które były tam przy użyciu getClass().getResource(), ale próbując pobrać nowo utworzony plik, otrzymałem pusty obiekt. Przeglądanie katalogu wyraźnie pokazuje, że jest tam nowy plik. Nazwy plików zostały poprzedzone ukośnikiem jak w „/myFile.txt”.

Z drugiej strony ClassLoaderwersja getResource()znalazła wygenerowany plik. Z tego doświadczenia wynika, że ​​trwa pewnego rodzaju buforowanie listy katalogów. Czy mam rację, a jeśli tak, gdzie to jest udokumentowane?

Z dokumentacji API naClass.getResource()

Znajduje zasób o podanej nazwie. Reguły wyszukiwania zasobów związanych z daną klasą są implementowane przez definiujący moduł ładujący klasę. Ta metoda deleguje się do modułu ładującego klasy tego obiektu. Jeśli ten obiekt został załadowany przez moduł ładujący klasę ładowania początkowego, metoda deleguje się do ClassLoader.getSystemResource (java.lang.String).

Dla mnie brzmi to: „Class.getResource naprawdę wywołuje funkcję getResource () własnego modułu ładującego”. Co byłoby równoznaczne z robieniem getClass().getClassLoader().getResource(). Ale oczywiście nie jest. Czy ktoś mógłby mi pomóc w tej kwestii?

oligofren
źródło

Odpowiedzi:

6

Aby odpowiedzieć na pytanie, czy trwa buforowanie.

Zbadałem ten punkt dalej, uruchamiając autonomiczną aplikację Java, która ciągle ładuje plik z dysku przy użyciu metody getResourceAsStream ClassLoader. Byłem w stanie edytować plik, a zmiany zostały natychmiast odzwierciedlone, tzn. Plik został ponownie załadowany z dysku bez buforowania.

Jednak: Pracuję nad projektem z kilkoma modułami maven i projektami internetowymi, które są od siebie zależne. Używam IntelliJ jako mojego IDE do kompilacji i uruchamiania projektów internetowych.

Zauważyłem, że powyższe wydaje się już nie obowiązywać, ponieważ plik, który ładowałem, jest teraz wypalany w słoiku i wdrażany w zależności od projektu internetowego. Zauważyłem to dopiero po próbie zmiany pliku w folderze docelowym, ale bezskutecznie. To sprawiało, że wydawało się, że trwa buforowanie.

mchlstckl
źródło
Ja również użyłem Maven i IntelliJ, więc jest to odpowiedź na środowisko, które najbardziej pasuje do mojego i ma uzasadnione wytłumaczenie na pytanie nr 2.
oligofren
250

Class.getResourcemoże przyjąć „względną” nazwę zasobu, która jest traktowana względem pakietu klasy. Alternatywnie możesz określić „bezwzględną” nazwę zasobu, używając początkowego ukośnika. Ścieżki zasobów Classloader są zawsze uważane za absolutne.

Zatem poniższe są w zasadzie równoważne:

foo.bar.Baz.class.getResource("xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt");

Podobnie jak te (ale różnią się od powyższych):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Jon Skeet
źródło
Ładna odpowiedź z jasnymi przykładami. Chociaż post miał w rzeczywistości uzyskać odpowiedzi na dwa pytania, teraz widzę, że drugie pytanie jest jakby ukryte. Całkiem niepewny, jak / czy powinienem zaktualizować post, aby to odzwierciedlić, ale chciałbym wiedzieć drugi: (następny komentarz):
oligofren
2
Czy w wersji Class.getResource () odbywa się buforowanie? To, co sprawiło, że uwierzyłem, to generowanie niektórych raportów jasper: Używamy getClass (). GetResource („/ aDocument.jrxml”), aby pobrać plik xml jasper. Plik binarny jaspis jest następnie tworzony w tym samym katalogu. getClass (). getResource ("/ aDocument.jasper") nie jest w stanie go znaleźć, chociaż może wyraźnie znaleźć dokumenty na tym samym poziomie (plik wejściowy). To tutaj okazało się pomocne ClassLoader.getResource (), ponieważ wydaje się, że nie używa buforowania listy katalogów. Ale nie mogę znaleźć dokumentacji na ten temat.
oligofren
2
@oligofren: Hmm ... nie spodziewałbym się, że Class.getResource () zrobi tam buforowanie ...
Jon Skeet
1
@JonSkeet po co this.getClass().getClassLoader().getResource("/");zwracać null? Nie powinno to być takie samo jakthis.getClass().getClassLoader().getResource(".");
Asif Mushtaq
@UnKnown: Myślę, że powinieneś prawdopodobnie zadać nowe pytanie na ten temat.
Jon Skeet
22

Pierwsze połączenie jest wyszukiwane względem .classpliku, a drugie - względem katalogu głównego ścieżki klasy.

Aby debugować takie problemy, drukuję adres URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Aaron Digulla
źródło
3
Myślę, że „root rootloader” byłby bardziej dokładny niż „root classpath” - po prostu wybredny.
Jon Skeet,
4
Oba mogą szukać „absolutne ścieżki” Jeśli nazwa pliku jest poprzedzany przez „/”
oligofren
2
Interesujące ... Uderzam w przypadek, w którym getClass (). GetResource ("/ someAbsPath") zwraca adres URL typu /path/to/mylib.jar!/someAbsPath i getClass (). GetClassLoafer (). GetResource (" / someAbsPath ”) zwraca null ... Więc„ root rootloadera ”nie wydaje się być bardzo dobrze zdefiniowanym pojęciem
Pierre Henry
8
@PierreHenry: getClassLoader().getResource("/...")zawsze zwraca null- moduł ładujący klasy nie usuwa wiodącego /elementu ze ścieżki, więc wyszukiwanie zawsze kończy się niepowodzeniem. Tylko getClass().getResource()obsługuje uruchamianie /jako ścieżka bezwzględna w stosunku do ścieżki klasy.
Aaron Digulla
17

Musiałem to sprawdzić w specyfikacjach:

Klasa getResource () - dokumentacja podaje różnicę:

Ta metoda deleguje wywołanie do modułu ładującego klasy, po wprowadzeniu tych zmian w nazwie zasobu: jeśli nazwa zasobu zaczyna się od „/”, pozostaje niezmieniona; w przeciwnym razie nazwa pakietu jest dodawana do nazwy zasobu po konwersji „.” do "/". Jeśli ten obiekt został załadowany przez moduł ładujący bootstrap, wywołanie jest delegowane do ClassLoader.getSystemResource.

Bernd Elkemann
źródło
2
Czy masz jakieś informacje na temat tego, czy buforuje również listę katalogów? To była główna różnica między tymi dwiema metodami, kiedy najpierw szukałem pliku wejściowego, a następnie tworzyłem plik, używając go w tym samym katalogu. Wersja Class go nie znalazła, wersja ClassLoader tak (obie używają „/file.txt”).
oligofren
11

Wszystkie te odpowiedzi tutaj, a także odpowiedzi na to pytanie sugerują, że ładowanie bezwzględnych adresów URL, takich jak „/foo/bar.properties”, traktowane było tak samo przez class.getResourceAsStream(String)i class.getClassLoader().getResourceAsStream(String). Tak NIE jest, przynajmniej nie w mojej konfiguracji / wersji Tomcat (obecnie 7.0.40).

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

Przepraszam, nie mam absolutnie żadnego satysfakcjonującego wyjaśnienia, ale wydaje mi się, że kocur robi brudne sztuczki i swoją czarną magię z modułami ładującymi klasy i powoduje różnicę. Zawsze używałem class.getResourceAsStream(String)w przeszłości i nie miałem żadnych problemów.

PS: Tutaj też to opublikowałem

Tim Büthe
źródło
To zachowanie wydaje się być błędem w Tomcat, który został naprawiony w wersji 8. Dodałem akapit na ten temat w mojej odpowiedzi na to pytanie
LordOfThePigs
2

Class.getResourcespobierze zasób przez moduł ładujący klasę, który ładuje obiekt. Podczas gdy ClassLoader.getResourcepobierałby zasób za pomocą określonego modułu ładującego.

lwpro2
źródło
0

Próbowałem czytać z input1.txt, który był w jednym z moich pakietów wraz z klasą, która próbowała go odczytać.

Następujące prace:

String fileName = FileTransferClient.class.getResource("input1.txt").getPath();

System.out.println(fileName);

BufferedReader bufferedTextIn = new BufferedReader(new FileReader(fileName));

Najważniejszą częścią było wywołanie, getPath()jeśli chcesz mieć poprawną nazwę ścieżki w formacie String. NIE UŻYWAJ,toString() ponieważ spowoduje to dodanie dodatkowego tekstu formatującego, który CAŁKOWICIE PRZESYŁA nazwę pliku (możesz wypróbować i zobaczyć wydruk).

Spędziłem 2 godziny na debugowaniu tego ... :(

Kevin Lee
źródło
Zasoby nie są plikami. Mogą nie być rozpakowany z pliku JAR lub WAR, a jeśli nie jest FileReaderlub FileInputStreamnie może być użyty do nich dostęp. Odpowiedź jest nieprawidłowa.
Markiz Lorne