хотя можно утверждать, что это основано на мнении, мне любопытно, есть ли у кого-то здесь представление об этом:
Предположим, вы пишете (узкоспециализированный, но не имеет значения) сервер приложений, такой как Tomcat, Jetty, ... для веб-приложений (та же базовая проблема). Все они имеют механизмы загрузки ресурсов, обычно использующие загрузчик классов контекста потока (Tomcat) или какую-то собственную схему (классы ресурсов Spring, ...).
Я бы хотел (но пока не нашел решение elegenat) предоставить что-то вроде этого, но, кроме того, разрешить конфигурирование во время выполнения каталога "переопределения": подумайте над реализацией Tomcat, но с учетом " .conf "файл рядом с каждым" .war ", который позволяет указывать каталог. Когда ресурс загружается, он сначала ищется в этом каталоге (за войну / приложение) и, если не найден, разрешается с помощью загрузчика классов приложения (в Tomcat, через Thread.currentThread (). GetContextClassloader () - хотя я лично также посчитайте это решение неэффективным, так как не все сторонние библиотеки используют загрузчик классов контекста, и если вы используете одну из них, вам, в принципе, не повезло).
Для этого потребуется доступ к пользовательскому URLStreamHandler или URLConnection во время выполнения. Это, конечно, возможно при «подходе Tomcat» - просто вызвать URL.setURLStreamHandlerFactory с одноэлементным экземпляром и настроить что-то вроде переменной ThreadLocal в этом одноэлементном экземпляре, прежде чем вызывать что-либо (например, весь механизм javax.xml для анализа / проверки), использующий URL для разрешения «ссылок». Теперь, как все знают, синглтоны / глобальное состояние - это проклятие автоматизированного тестирования, но я просто не могу найти решение этого, не обращаясь к какому-то глобальному состоянию.
Вот решение, которое я нашел работающим, которое, к сожалению, использует глобальное состояние:
public class XmlParser {
// this is the global state...
private static com.example.proto.classpath.Handler cpHandler;
static {
cpHandler = new com.example.proto.classpath.Handler();
URL.setURLStreamHandlerFactory(p -> proto.equalsIgnoreCase("example") ? cpHandler : null);
}
public static interface ResourceLocator extends Function<String, URL> {};
public org.w3c.dom.Element parse(InputStream doc, ResourceLocator locator) {
ResourceLocator old = cpHandler.setResourceLocator(locator);
try {
// do the actual parsing, not relevant here
return doc.getDocumentElement();
} finally {
cpHandler.setResourceLocator(old);
}
}
}
с обработчиком
public class Handler {
private ThreadLocal<ResourceLocator> loc;
@Override
protected URLConnection openConnection(URL url) throws IOException {
return loc.get().apply(url.getPath()).openConnection();
}
public ResourceLocator setResourceLocator(ResourceLocator loc) {
ResourceLocator old = this.loc.get();
this.loc.set(loc);
return old;
}
}
Таким образом, вы можете реализовать множество ResourceLocators, используя любой желаемый загрузчик классов, сколько угодно каталогов переопределения и т. Д., Но у вас есть глобальное состояние в форме переменной cpHandler XmlParser.
Я бы хотел найти способ избавиться от глобального состояния. У какого-нибудь умного ума на этом сайте есть идея, как достигнуть этого в Java?
P.S .: В настоящее время я пытаюсь сделать это на Java 11 (поскольку это текущий LTS), поэтому JPMS также играет роль в моей текущей реализации ResourceLocator, но здесь это не имеет значения. Также не против обновления до Java 12 или раннего доступа 13 или чего-то еще, если это поможет.