Sự khác biệt giữa Class.getResource () và ClassLoader.getResource () là gì?


194

Tôi tự hỏi sự khác biệt giữa Class.getResource()ClassLoader.getResource()?

chỉnh sửa: Tôi đặc biệt muốn biết nếu có bất kỳ bộ nhớ đệm có liên quan đến cấp độ tập tin / thư mục. Như trong "danh sách thư mục được lưu trong bộ đệm Class?"

AFAIK sau đây về cơ bản nên làm như vậy, nhưng chúng không phải là:

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

Tôi đã phát hiện ra điều này khi đấu tranh với một số mã tạo báo cáo tạo ra một tệp mới WEB-INF/classes/từ một tệp hiện có trong thư mục đó. Khi sử dụng phương thức từ Class, tôi có thể tìm thấy các tệp đang triển khai bằng cách sử dụng getClass().getResource(), nhưng khi cố gắng tìm nạp tệp vừa tạo, tôi đã nhận được một đối tượng null. Duyệt thư mục rõ ràng cho thấy các tập tin mới là ở đó. Tên tệp được thêm vào bằng dấu gạch chéo về phía trước như trong "/myFile.txt".

Các ClassLoaderphiên bản của getResource()mặt khác đã tìm thấy các tập tin được tạo ra. Từ kinh nghiệm này, có vẻ như có một số loại bộ nhớ đệm của danh sách thư mục đang diễn ra. Tôi có đúng không, và nếu vậy, tài liệu này ở đâu?

Từ các tài liệu API trênClass.getResource()

Tìm một tài nguyên với một tên nhất định. Các quy tắc tìm kiếm tài nguyên được liên kết với một lớp nhất định được triển khai bởi trình nạp lớp xác định của lớp. Phương thức này ủy quyền cho trình nạp lớp của đối tượng này. Nếu đối tượng này được tải bởi trình nạp lớp bootstrap, phương thức sẽ ủy quyền cho ClassLoader.getSystemResource (java.lang.String).

Đối với tôi, điều này có nghĩa là "Class.getResource thực sự đang gọi getResource () của trình nạp lớp riêng của nó. Mà sẽ giống như làm getClass().getClassLoader().getResource(). Nhưng rõ ràng là không. Ai đó có thể vui lòng cung cấp cho tôi một số chiếu sáng về vấn đề này?

Câu trả lời:


6

Để trả lời câu hỏi liệu có bất kỳ bộ nhớ đệm đang diễn ra.

Tôi đã nghiên cứu điểm này hơn nữa bằng cách chạy một ứng dụng Java độc lập, liên tục tải một tệp từ đĩa bằng phương thức getResourceAsStream ClassLoader. Tôi đã có thể chỉnh sửa tệp và các thay đổi đã được phản ánh ngay lập tức, tức là tệp đã được tải lại từ đĩa mà không cần lưu vào bộ đệm.

Tuy nhiên: Tôi đang làm việc trên một dự án với một số mô-đun maven và các dự án web có sự phụ thuộc lẫn nhau. Tôi đang sử dụng IntelliJ làm IDE của mình để biên dịch và chạy các dự án web.

Tôi nhận thấy rằng những điều trên dường như không còn đúng nữa, lý do là tập tin mà tôi đang tải hiện được nướng vào một cái bình và triển khai cho dự án web tùy thuộc. Tôi chỉ nhận thấy điều này sau khi cố gắng thay đổi tệp trong thư mục đích của mình, nhưng không có kết quả. Điều này làm cho nó có vẻ như có bộ nhớ đệm đang diễn ra.


Tôi cũng đã sử dụng Maven và IntelliJ, vì vậy đây là câu trả lời với một môi trường phù hợp nhất với tôi và có lời giải thích hợp lý cho câu hỏi số 2.
oligofren

249

Class.getResourcecó thể lấy tên tài nguyên "tương đối", được xử lý tương đối với gói của lớp. Ngoài ra, bạn có thể chỉ định tên tài nguyên "tuyệt đối" bằng cách sử dụng dấu gạch chéo hàng đầu. Đường dẫn tài nguyên của trình nạp lớp luôn được coi là tuyệt đối.

Vì vậy, sau đây về cơ bản là tương đương:

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

Và những thứ này cũng vậy (nhưng chúng khác với ở trên):

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

Câu trả lời tốt đẹp với các ví dụ rõ ràng. Mặc dù bài đăng thực sự có nghĩa là để có câu trả lời cho hai câu hỏi, nhưng tôi thấy bây giờ các câu hỏi thứ hai là loại ẩn. Khá không chắc chắn về cách / liệu tôi có nên cập nhật bài đăng để phản ánh điều này hay không, nhưng điều tôi muốn biết thứ hai là đây (bình luận tiếp theo):
oligofren

2
Có một số loại bộ nhớ đệm đang diễn ra trong phiên bản Class.getResource ()? Điều khiến tôi tin rằng đây là việc tạo ra một số báo cáo jasper: Chúng tôi sử dụng getClass (). GetResource ("/ aDocument.jrxml") để tìm nạp tệp jmlper xml. Một tệp jasper nhị phân sau đó được sản xuất trong cùng một thư mục. getClass (). getResource ("/ aDocument.jasper") không thể tìm thấy nó, mặc dù nó có thể tìm thấy rõ ràng các tài liệu ở cùng cấp độ (tệp đầu vào). Đây là nơi ClassLoader.getResource () tỏ ra hữu ích, vì có vẻ như nó không sử dụng bộ nhớ đệm của danh sách thư mục. Nhưng tôi không thể tìm thấy tài liệu về điều này.
oligofren

2
@oligofren: Hmm ... Tôi sẽ không mong đợi Class.getResource () thực hiện bất kỳ bộ nhớ đệm nào ở đó ...
Jon Skeet

1
@JonSkeet tại sao this.getClass().getClassLoader().getResource("/");trả về null? Nó không giống nhưthis.getClass().getClassLoader().getResource(".");
Asif Mushtaq

@UnKnown: Tôi nghĩ có lẽ bạn nên hỏi một câu hỏi mới về điều đó.
Jon Skeet

22

Các cuộc gọi đầu tiên tìm kiếm liên quan đến .class tệp trong khi cuộc gọi sau tìm kiếm liên quan đến gốc đường dẫn lớp.

Để gỡ lỗi các vấn đề như vậy, tôi in URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );

3
Tôi nghĩ rằng "rootloader root" sẽ chính xác hơn "rootpath class" - chỉ để được kén chọn.
Jon Skeet

4
Cả hai đều có thể tìm kiếm "đường dẫn tuyệt đối" nếu tên tệp được đặt trước bởi "/"
oligofren

2
Thật thú vị ... Tôi đang gặp trường hợp getClass (). GetResource ("/ someAbsPath") trả về một URL trong loại /path/to/mylib.jar!/someAbsPath và getClass (). GetClassLoafer (). GetRlassource (). / someAbsPath ") trả về null ... Vì vậy," root of classloader "dường như không phải là một khái niệm được xác định rõ ràng ...
Pierre Henry


8
@PierreHenry: getClassLoader().getResource("/...")luôn trả về null- trình nạp lớp không xóa phần dẫn /khỏi đường dẫn, vì vậy việc tra cứu luôn thất bại. Chỉ getClass().getResource()xử lý một khởi đầu /như một đường dẫn tuyệt đối liên quan đến đường dẫn lớp.
Aaron Digulla

17

Phải tìm kiếm nó trong thông số kỹ thuật:

Class 'getResource () - tài liệu nêu rõ sự khác biệt:

Phương thức này ủy thác cuộc gọi đến trình nạp lớp của nó, sau khi thực hiện các thay đổi này cho tên tài nguyên: nếu tên tài nguyên bắt đầu bằng "/", thì nó không thay đổi; mặt khác, tên gói được thêm vào tên tài nguyên sau khi chuyển đổi "." đến "/". Nếu đối tượng này được tải bởi trình tải bootstrap, cuộc gọi được ủy quyền cho ClassLoader.getSystemResource.


2
Bạn có bất kỳ thông tin nào về việc nó có lưu trữ danh sách thư mục không? Đây là sự khác biệt chính giữa hai phương thức khi lần đầu tiên tìm kiếm một tệp đầu vào, sau đó tạo một tệp bằng cách sử dụng tệp đó trong cùng một thư mục. Phiên bản Class không tìm thấy, phiên bản ClassLoader đã làm (cả hai đều sử dụng "/file.txt").
oligofren

11

Tất cả những câu trả lời quanh đây, cũng như câu trả lời trong câu hỏi này , đề nghị rằng việc tải các URL tuyệt đối, như "/foo/bar.properies" cũng được xử lý như vậy class.getResourceAsStream(String)class.getClassLoader().getResourceAsStream(String). Đây không phải là trường hợp, ít nhất là không có trong cấu hình / phiên bản Tomcat của tôi (hiện là 7.0.40).

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

Xin lỗi, tôi hoàn toàn không có lời giải thích thỏa mãn, nhưng tôi đoán rằng tomcat thực hiện các thủ đoạn bẩn thỉu và ma thuật đen của anh ta với các trình nạp lớp và gây ra sự khác biệt. Tôi luôn luôn sử dụng class.getResourceAsStream(String)trong quá khứ và không có vấn đề gì.

PS: Tôi cũng đã đăng cái này lên đây


Hành vi này dường như là một lỗi trong Tomcat đã được sửa trong phiên bản 8. Tôi đã thêm một đoạn về câu trả lời
LordOfThePigs

2

Class.getResourcessẽ lấy tài nguyên bởi trình nạp lớp tải đối tượng. Trong khi ClassLoader.getResourcesẽ lấy tài nguyên bằng cách sử dụng trình nạp lớp được chỉ định.


0

Tôi đã thử đọc từ input1.txt, một trong các gói của tôi cùng với lớp đang cố đọc nó.

Các công việc sau đây:

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

System.out.println(fileName);

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

Phần quan trọng nhất là gọi getPath()nếu bạn muốn tên đường dẫn chính xác ở định dạng Chuỗi. KHÔNG SỬ DỤNGtoString() vì nó sẽ thêm một số văn bản định dạng bổ sung sẽ HOÀN TOÀN TIN NHẮN tên tệp (bạn có thể dùng thử và xem bản in ra).

Đã dành 2 giờ để gỡ lỗi này ... :(



Tài nguyên không phải là tập tin. Chúng có thể không được giải nén khỏi tệp JAR hoặc WAR và nếu không FileReaderhoặc FileInputStreamkhông thể sử dụng để truy cập chúng. Trả lời không đúng.
Hầu tước Lorne
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.