Введение и базовая реализация
Сначала вам понадобится хотя бы URLStreamHandler. Это фактически откроет соединение с данным URL. Обратите внимание, что это просто называется Handler
; это позволяет вам указать java -Djava.protocol.handler.pkgs=org.my.protocols
, и он будет автоматически выбран, используя «простое» имя пакета в качестве поддерживаемого протокола (в данном случае «classpath»).
Использование
new URL("classpath:org/my/package/resource.extension").openConnection();
Код
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();
}
}
Проблемы с запуском
Если вы чем-то похожи на меня, вы не хотите полагаться на свойство, установленное при запуске, чтобы получить вас куда-то (в моем случае, мне нравится держать свои параметры открытыми как Java WebStart - вот почему
мне нужно все это).
обходные пути / Улучшение
Ручной код Спецификация обработчика
Если вы контролируете код, вы можете сделать
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
и это будет использовать ваш обработчик, чтобы открыть соединение.
Но опять же, это менее чем удовлетворительно, так как вам не нужен URL для этого - вы хотите сделать это, потому что какая-то библиотека, которую вы не можете (или не хотите) контролировать, требует URL ...
Регистрация обработчика JVM
Окончательный вариант - зарегистрировать URLStreamHandlerFactory
, который будет обрабатывать все URL через 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);
}
}
Чтобы зарегистрировать обработчик, позвоните по номеру URL.setURLStreamHandlerFactory()
, настроенному на вашем заводе. Затем выполните new URL("classpath:org/my/package/resource.extension")
, как в первом примере, и все готово.
Проблема регистрации обработчика JVM
Обратите внимание, что этот метод может вызываться только один раз для JVM, и обратите внимание, что Tomcat будет использовать этот метод для регистрации обработчика JNDI (AFAIK). Попробуйте Jetty (я буду); в худшем случае вы можете сначала использовать метод, а затем он должен работать вокруг вас!
Лицензия
Я передаю это в общественное достояние и спрашиваю, что если вы хотите изменить это, вы запускаете проект OSS где-то и комментируете здесь с деталями. Лучшей реализацией было бы иметь URLStreamHandlerFactory
, который использует ThreadLocal
с для хранения URLStreamHandler
с для каждого Thread.currentThread().getContextClassLoader()
. Я даже дам вам свои модификации и тестовые классы.