Implementasi Intro dan dasar
Pertama, Anda membutuhkan setidaknya URLStreamHandler. Ini sebenarnya akan membuka koneksi ke URL yang diberikan. Perhatikan bahwa ini hanya disebut Handler
; ini memungkinkan Anda untuk menentukan java -Djava.protocol.handler.pkgs=org.my.protocols
dan secara otomatis akan diambil, menggunakan nama paket "sederhana" sebagai protokol yang didukung (dalam hal ini "classpath").
Pemakaian
new URL("classpath:org/my/package/resource.extension").openConnection();
Kode
package org.my.protocols.classpath;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
/** A {@link URLStreamHandler} that handles resources on the classpath. */
public class Handler extends URLStreamHandler {
/** The classloader to find resources from. */
private final ClassLoader classLoader;
public Handler() {
this.classLoader = getClass().getClassLoader();
}
public Handler(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
protected URLConnection openConnection(URL u) throws IOException {
final URL resourceUrl = classLoader.getResource(u.getPath());
return resourceUrl.openConnection();
}
}
Luncurkan masalah
Jika Anda seperti saya, Anda tidak ingin bergantung pada properti yang diatur dalam peluncuran untuk membawa Anda ke suatu tempat (dalam kasus saya, saya ingin menjaga opsi saya tetap terbuka seperti Java WebStart - itulah sebabnya
saya membutuhkan semua ini ).
Penanganan / Peningkatan
Spesifikasi Penangan kode manual
Jika Anda mengontrol kode, Anda dapat melakukannya
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
dan ini akan menggunakan handler Anda untuk membuka koneksi.
Tapi sekali lagi, ini kurang memuaskan, karena Anda tidak perlu URL untuk melakukan ini - Anda ingin melakukan ini karena beberapa lib Anda tidak dapat (atau tidak ingin) mengendalikan keinginan url ...
Pendaftaran JVM Handler
Opsi terakhir adalah mendaftar URLStreamHandlerFactory
yang akan menangani semua url di jvm:
package my.org.url;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.HashMap;
import java.util.Map;
class ConfigurableStreamHandlerFactory implements URLStreamHandlerFactory {
private final Map<String, URLStreamHandler> protocolHandlers;
public ConfigurableStreamHandlerFactory(String protocol, URLStreamHandler urlHandler) {
protocolHandlers = new HashMap<String, URLStreamHandler>();
addHandler(protocol, urlHandler);
}
public void addHandler(String protocol, URLStreamHandler urlHandler) {
protocolHandlers.put(protocol, urlHandler);
}
public URLStreamHandler createURLStreamHandler(String protocol) {
return protocolHandlers.get(protocol);
}
}
Untuk mendaftarkan pawang, hubungi URL.setURLStreamHandlerFactory()
pabrik Anda yang sudah dikonfigurasi. Kemudian lakukan new URL("classpath:org/my/package/resource.extension")
seperti contoh pertama dan Anda pergi.
Masalah Pendaftaran Handler JVM
Perhatikan bahwa metode ini hanya dapat dipanggil sekali per JVM, dan perhatikan juga bahwa Tomcat akan menggunakan metode ini untuk mendaftarkan penangan JNDI (AFAIK). Coba Jetty (saya akan); paling buruk, Anda dapat menggunakan metode ini terlebih dahulu dan kemudian harus bekerja di sekitar Anda!
Lisensi
Saya merilis ini ke domain publik, dan meminta jika Anda ingin memodifikasi bahwa Anda memulai proyek OSS di suatu tempat dan berkomentar di sini dengan detailnya. Implementasi yang lebih baik adalah memiliki URLStreamHandlerFactory
yang menggunakan ThreadLocal
s untuk menyimpan URLStreamHandler
masing-masing Thread.currentThread().getContextClassLoader()
. Saya bahkan akan memberi Anda kelas modifikasi dan tes saya.
com.github.fommil.common-utils
paket saya yang saya berencana untuk perbarui dan rilis segera melalui Sonatype.System.setProperty()
untuk mendaftarkan protokol. SepertiSystem.setProperty("java.protocol.handler.pkgs", "org.my.protocols");
Itu harus dilakukan.
sumber
Saya pikir ini layak untuk dijawab sendiri - jika Anda menggunakan Spring, Anda sudah memilikinya
Seperti dijelaskan dalam dokumentasi musim semi dan ditunjukkan dalam komentar oleh skaffman.
sumber
ResourceLoader.getResource()
lebih tepat untuk tugas ini (ApplicationContext.getResource()
delegasi di bawah tenda)Anda juga dapat mengatur properti secara terprogram saat startup:
Menggunakan kelas ini:
Dengan demikian Anda mendapatkan cara yang paling tidak mengganggu untuk melakukan ini. :) java.net.URL akan selalu menggunakan nilai saat ini dari properti sistem.
sumber
java.protocol.handler.pkgs
variabel sistem hanya dapat digunakan jika pawang bertujuan untuk memproses protokol yang belum "dikenal" sepertigopher://
. Jika tujuannya adalah untuk mengesampingkan protokol "populer", sukafile://
atauhttp://
, mungkin sudah terlambat untuk melakukannya, karenajava.net.URL#handlers
peta sudah menambahkan penangan "standar" untuk protokol itu. Jadi satu-satunya jalan keluar adalah meneruskan variabel ini ke JVM.(Mirip dengan jawaban Azder , tetapi kebijaksanaan yang sedikit berbeda.)
Saya tidak percaya ada pengendali protokol yang telah ditentukan untuk konten dari classpath. (
classpath:
Protokol yang disebut ).Namun, Java memungkinkan Anda untuk menambahkan protokol Anda sendiri. Ini dilakukan dengan memberikan implementasi konkret
java.net.URLStreamHandler
danjava.net.URLConnection
.Artikel ini menjelaskan bagaimana penangan aliran kustom dapat diimplementasikan: http://java.sun.com/developer/onlineTraining/protocolhandlers/ .
sumber
Saya telah membuat kelas yang membantu mengurangi kesalahan dalam mengatur penangan khusus dan memanfaatkan properti sistem sehingga tidak ada masalah dengan memanggil metode terlebih dahulu atau tidak berada di wadah yang tepat. Ada juga kelas pengecualian jika Anda salah:
sumber
Terinspirasi oleh @Stephen https://stackoverflow.com/a/1769454/980442 dan http://docstore.mik.ua/orelly/java/exp/ch09_06.htm
Menggunakan
buat saja kelas ini ke dalam
sun.net.www.protocol.classpath
paket dan jalankan ke implementasi Oracle JVM agar bekerja seperti pesona.Jika Anda menggunakan implementasi JVM lain, atur
java.protocol.handler.pkgs=sun.net.www.protocol
properti sistem.FYI: http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL(java.lang.String,%20java.lang.String , %20int ,% 20java.lang .Tali)
sumber
Solusi dengan mendaftarkan URLStreamHandlers tentu saja paling benar, tetapi kadang-kadang diperlukan solusi paling sederhana. Jadi, saya menggunakan metode berikut untuk itu:
sumber
Dari Java 9+ dan lebih tinggi, Anda dapat mendefinisikan yang baru
URLStreamHandlerProvider
. TheURL
kelas menggunakan kerangka loader layanan untuk memuatnya pada jangka waktu.Buat penyedia:
Buat file yang disebut
java.net.spi.URLStreamHandlerProvider
diMETA-INF/services
direktori dengan konten:Sekarang kelas URL akan menggunakan penyedia ketika melihat sesuatu seperti:
sumber
Saya tidak tahu apakah sudah ada, tetapi Anda dapat membuatnya sendiri dengan mudah.
Contoh protokol yang berbeda terlihat bagi saya seperti pola fasad. Anda memiliki antarmuka umum ketika ada implementasi yang berbeda untuk setiap kasus.
Anda bisa menggunakan prinsip yang sama, membuat kelas ResourceLoader yang mengambil string dari file properti Anda, dan memeriksa protokol khusus kami
strip myprotocol: dari awal string dan kemudian membuat keputusan cara memuat sumber daya, dan hanya memberi Anda sumber daya.
sumber
Perpanjangan untuk jawaban Dilums :
Tanpa mengubah kode, Anda mungkin perlu mengejar implementasi khusus antarmuka terkait URL seperti yang disarankan Dilum. Untuk menyederhanakan hal-hal untuk Anda, saya dapat merekomendasikan melihat sumber untuk Sumber Daya Spring Framework . Walaupun kodenya tidak dalam bentuk stream handler, ia telah dirancang untuk melakukan apa yang Anda ingin lakukan dan berada di bawah lisensi ASL 2.0, membuatnya cukup ramah untuk digunakan kembali dalam kode Anda dengan kredit jatuh tempo.
sumber
Dalam aplikasi Spring Boot, saya menggunakan yang berikut ini untuk mendapatkan URL file,
sumber
Jika Anda memiliki kucing jantan di classpath, itu sesederhana:
Ini akan mendaftarkan penangan untuk protokol "perang" dan "classpath".
sumber
Saya mencoba menghindari
URL
kelas dan bukannya mengandalkanURI
. Jadi untuk hal-hal yang perlu diURL
mana saya ingin melakukan Spring Resource seperti mencari tanpa Spring saya melakukan hal berikut:Untuk membuat URI bisa Anda gunakan
URI.create(..)
. Cara ini juga lebih baik karena Anda mengontrolClassLoader
yang akan melakukan pencarian sumber daya.Saya perhatikan beberapa jawaban lain yang mencoba mengurai URL sebagai String untuk mendeteksi skema. Saya pikir lebih baik untuk menyebarkan URI dan menggunakannya untuk menguraikan.
Saya sebenarnya telah mengajukan masalah beberapa saat yang lalu dengan Spring Source memohon mereka untuk memisahkan kode Sumber Daya mereka
core
sehingga Anda tidak membutuhkan semua hal-hal Musim Semi lainnya.sumber