获取对 Java 的默认 http(s) URLStreamHandler 的引用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18018628/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getting a reference to Java's default http(s) URLStreamHandler
提问by Markus A.
I have a library that I need to use in one of my projects, which unfortunately registers its own URLStreamHandlerto handle http-URLs. Is there a way to get a reference to Java's default http- and https-URLStreamHandlers, so I can specify one of them in the URL's constructor to open a standard http connection without using the protocol overridden by the library?
我有一个需要在我的一个项目中使用的库,不幸的是它注册了自己的库URLStreamHandler来处理 http- URLs。有没有办法获得对 Java 的默认 http- 和 https- 的引用URLStreamHandlers,以便我可以在URL构造函数中指定其中一个来打开标准的 http 连接,而无需使用库覆盖的协议?
采纳答案by Markus A.
Found it:
找到了:
sun.net.www.protocol.http.Handler
With that, I can now do:
有了这个,我现在可以做:
URL url = new URL(null, "http://...", new sun.net.www.protocol.http.Handler());
HttpURLConnection cxn = (HttpURLConnection) url.openConnection();
And I get a normal Java HttpURLConnection and not the one provided by the library.
我得到了一个普通的 Java HttpURLConnection 而不是库提供的那个。
Update:
更新:
I discovered another, more general, approach: Remove the library's URLStreamHandlerFactory!
我发现了另一种更通用的方法:删除库的URLStreamHandlerFactory!
This is a bit tricky, since the URL-class technically doesn't allow you to set the factory more than once or to clear it with an official function call, but with a bit of reflection-magic, we can do it anyways:
这有点棘手,因为 URL 类在技术上不允许您多次设置工厂或通过官方函数调用清除它,但是通过一些反射魔法,我们无论如何都可以做到:
public static String unsetURLStreamHandlerFactory() {
try {
Field f = URL.class.getDeclaredField("factory");
f.setAccessible(true);
Object curFac = f.get(null);
f.set(null, null);
URL.setURLStreamHandlerFactory(null);
return curFac.getClass().getName();
} catch (Exception e) {
return null;
}
}
This method grabs a hold of the static field factoryin the URL-class, makes it accessible, grabs its current value and changes it to null. Afterwards, it calls URL.setStreamHandlerFactory(null)(which now completes without error) to make this setting "official", i.e. give the function a chance to do any other clean-up that it might want to do. Then it returns the previously registered factory's class name, just for reference. If anything goes wrong, it swallows the exception (I know, bad idea...) and returns null.
此方法factory获取URL-class中的静态字段,使其可访问,获取其当前值并将其更改为null。之后,它调用URL.setStreamHandlerFactory( null)(现在没有错误地完成)使这个设置成为“正式的”,即给函数一个机会做它可能想做的任何其他清理。然后返回之前注册的工厂的类名,仅供参考。如果出现任何问题,它会吞下异常(我知道,坏主意......)并返回null。
For reference: Here is the relevant source-code for URL.java.
Note:This approach might be even more risky than using the internal sun-classes (as far as portability goes) since it relies on the specific internal structure of the URL-class (namely the existence and exact function of the factory-field), but it does have the advantage that I don't need to go through all of my code to find all URL-constructors and add the handler-parameter... Also, it might break some functionality of the library that relies on their registered handlers. Luckily, neither issue (portability and partially broken library functionality) are issues that are relevant in my case.
注意:这种方法可能比使用内部 sun-classes 风险更大(就可移植性而言),因为它依赖于 URL-class 的特定内部结构(即factory-field的存在和确切功能),但是它的优点是我不需要遍历所有代码来查找所有 URL 构造函数并添加处理程序参数......此外,它可能会破坏依赖于其注册处理程序的库的某些功能。幸运的是,这两个问题(可移植性和部分损坏的库功能)都与我的情况无关。
Update: #2
更新:#2
While we're using reflection: Here is the probably safest way to get a reference to the default handler:
当我们使用反射时:以下是获取对默认处理程序的引用的最安全方法:
public static URLStreamHandler getURLStreamHandler(String protocol) {
try {
Method method = URL.class.getDeclaredMethod("getURLStreamHandler", String.class);
method.setAccessible(true);
return (URLStreamHandler) method.invoke(null, protocol);
} catch (Exception e) {
return null;
}
}
which you then simply call as:
然后您只需将其称为:
URLStreamHandler hander = getURLStreamHandler("http");
Note:This call needs to happen beforethe library registers its URLStreamHandlerFactory, otherwise you will end up with a reference to their handler.
注意:此调用需要在库注册其之前发生URLStreamHandlerFactory,否则您将最终获得对其处理程序的引用。
Why would I consider this the safest approach? Because URL.getURLStreamHandler(...)is not a fully private method, but only package-private. So, modifying its call-signature could break other code in the same package. Also, its name doesn't really leave much room for returning anything other than what we are looking for. Thus, I would expect it to be unlikely (albeit still not impossible) that a different/future implementation of the URL-class would be incompatible with the assumptions made here.
为什么我认为这是最安全的方法?因为URL.getURLStreamHandler(...)不是完全私有的方法,而只是包私有的。因此,修改其呼号可能会破坏同一包中的其他代码。此外,除了我们正在寻找的东西之外,它的名字并没有真正为返回任何东西留下太多空间。因此,我预计URL-class的不同/未来实现不太可能(尽管仍然不是不可能)与此处所做的假设不兼容。
回答by Luca Basso Ricci
You can register your own URLStreamHandlerFactorywirh URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory());
您可以注册自己的URLStreamHandlerFactorywirhURL.setURLStreamHandlerFactory(new URLStreamHandlerFactory());
public class URLStreamHandlerFactory implements java.net.URLStreamHandlerFactory {
public URLStreamHandlerFactory() {}
public URLStreamHandler createURLStreamHandler(String protocol) {
if(protocol.equals("http")) {
return new sun.net.www.protocol.http.Handler();
} else if(protocol.equals("https")) {
return new sun.net.www.protocol.https.Handler();
}
return null;
}
}
so you can use standard handler
所以你可以使用标准处理程序
EDIT: Found this code
编辑:找到此代码
回答by Tom Rutchik
Here's Markus A solution rewritten for Android:
这是 Markus 为 Android 重写的解决方案:
public static URLStreamHandler getURLStreamHandler(String protocolIdentifier) {
try {
URL url = new URL(protocolIdentifier);
Field handlerField = URL.class.getDeclaredField("handler");
handlerField.setAccessible(true);
return (URLStreamHandler)handlerField.get(url);
} catch (Exception e) {
return null;
}
}
The method getURLStreamHandler doesn't exist in android, so it needs to be done differently. The protocolIdentifier is the protocol name plus the colon. You need to pass in a value so that can be used to instantiate a URL instance for the protocol URLStreamHandler you want. If you want the standard "jar:" protocol handler you must enter something like this: "jar:file:!/". The "file" could be replaced with "http" or "https", as they all get you the same handler instance.
android 中不存在 getURLStreamHandler 方法,因此需要进行不同的处理。protocolIdentifier 是协议名称加上冒号。您需要传入一个值,以便可用于为所需的协议 URLStreamHandler 实例化 URL 实例。如果您想要标准的“jar:”协议处理程序,您必须输入如下内容:“jar:file:!/”。“文件”可以替换为“http”或“https”,因为它们都为您提供相同的处理程序实例。
The standard Handlers that android supports are http, https, file, jar, ftp.
android 支持的标准 Handlers 有 http、https、file、jar、ftp。

