JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持。当我们要利用这些协议来创建应用时,主要会涉及到如下几个类:java.net.URL、java.net.URLConnection、InputStream。URL类默认支持上述协议,但是有时候我们想自定义协议,怎么办呢?
Java提供了三种方法可以支持这个扩展
1、URL.setURLStreamHandlerFactory(URLStreamHandlerFactory) URLStreamHandlerFactory(java.net.URLStreamHandlerFactory),这是一个接口,定义如下:
package java.net;
/**
* This interface defines a factory for {@code URL} stream
* protocol handlers.
* * It is used by the {@code URL} class to create a
* {@code URLStreamHandler} for a specific protocol.
*
* @authorArthur van Hoff
* @seejava.net.URL
* @seejava.net.URLStreamHandler
* @sinceJDK1.0
*/
public interface URLStreamHandlerFactory {
/**
* Creates a new {@code URLStreamHandler} instance with the specified
* protocol.
*
* @paramprotocolthe protocol ("{@code ftp}",
*"{@code http}", "{@code nntp}", etc.).
* @returna {@code URLStreamHandler} for the specific protocol.
* @seejava.net.URLStreamHandler
*/
URLStreamHandler createURLStreamHandler(String protocol);
}
此接口需要实现createURLStreamHandler(String protocol)方法,参数protocol为协议名称,返回URLStreamHandler(java.net.URLStreamHandler)抽象类,抽象方法定义如下:
abstract protected URLConnection openConnection(URL u) throws IOException;
参数u为URL类型,URL.openConnection间接调用这个方法,返回URLConnection,然后可以获取InputStream进而获取相应的数据(资源)
示例如下:
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {if("json".equals(protocol)){
return new URLStreamHandler(){@Override
protected URLConnection openConnection(URL url) throws IOException {return new URLConnection(url){public InputStream getInputStream() throws IOException {
return new FileInputStream("d:/aaaa.txt");
}@Override
public void connect() throws IOException {
//建立连接
}
};
}
};
}
else return null;
}
});
URL url = new URL("json://json.url.com");
InputStream in = url.openConnection().getInputStream();
System.out.println(in.read());
上述代码判断如果协议(protocal)为json,则返回一个自定义的URLStreamHandler,否则返回null,对应其他Java本身已经支持的协议会不会造成影响呢?
我们且看URL的一个构造方法(URL(String protocol, String host, int port, String file,URLStreamHandler handler) 中类似):
public URL(URL context, String spec, URLStreamHandler handler)
throws MalformedURLException
{
String original = spec;
int i, limit, c;
int start = 0;
String newProtocol = null;
boolean aRef=false;
boolean isRelative = false;
// Check for permission to specify a handler
if (handler != null) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkSpecifyHandler(sm);
}
} try {
limit = spec.length();
while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
limit--;
//eliminate trailing whitespace
}
while ((start < limit) && (spec.charAt(start) <= ' ')) {
start++;
// eliminate leading whitespace
}if (spec.regionMatches(true, start, "url:", 0, 4)) {
start += 4;
}
if (start < spec.length() && spec.charAt(start) == '#') {
/* we're assuming this is a ref relative to the context URL.
* This means protocols cannot start w/ '#', but we must parse
* ref URL's like: "hello:there" w/ a ':' in them.
*/
aRef=true;
}
for (i = start ;
!aRef && (i < limit) &&
((c = spec.charAt(i)) != '/') ;
i++) {
if (c == ':') {String s = spec.substring(start, i).toLowerCase();
if (isValidProtocol(s)) {
newProtocol = s;
start = i + 1;
}
break;
}
}// Only use our context if the protocols match.
protocol = newProtocol;
if ((context != null) && ((newProtocol == null) ||
newProtocol.equalsIgnoreCase(context.protocol))) {
// inherit the protocol handler from the context
// if not specified to the constructor
if (handler == null) {
handler = context.handler;
}// If the context is a hierarchical URL scheme and the spec
// contains a matching scheme then maintain backwards
// compatibility and treat it as if the spec didn't contain
// the scheme;
see 5.2.3 of RFC2396
if (context.path != null && context.path.startsWith("/"))
newProtocol = null;
if (newProtocol == null) {
protocol = context.protocol;
authority = context.authority;
userInfo = context.userInfo;
host = context.host;
port = context.port;
file = context.file;
path = context.path;
isRelative = true;
}
}if (protocol == null) {
throw new MalformedURLException("no protocol: "+original);
}// Get the protocol handler if not specified or the protocol
// of the context could not be used
if (handler == null &&
(handler = getURLStreamHandler(protocol)) == null) {
throw new MalformedURLException("unknown protocol: "+protocol);
}this.handler = handler;
i = spec.indexOf('#', start);
if (i >= 0) {
ref = spec.substring(i + 1, limit);
limit = i;
}/*
* Handle special case inheritance of query and fragment
* implied by RFC2396 section 5.2.2.
*/
if (isRelative && start == limit) {
query = context.query;
if (ref == null) {
ref = context.ref;
}
}handler.parseURL(this, spec, start, limit);
} catch(MalformedURLException e) {
throw e;
} catch(Exception e) {
MalformedURLException exception = new MalformedURLException(e.getMessage());
exception.initCause(e);
throw exception;
}
}
代码87行,调用了getURLStreamHandler(protocol),此方法:
static URLStreamHandler getURLStreamHandler(String protocol) { URLStreamHandler handler = handlers.get(protocol);
if (handler == null) {boolean checkedWithFactory = false;
// Use the factory (if any)
if (factory != null) {
handler = factory.createURLStreamHandler(protocol);
checkedWithFactory = true;
}// Try java protocol handler
if (handler == null) {
String packagePrefixList = null;
packagePrefixList
= java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
protocolPathProp,""));
if (packagePrefixList != "") {
packagePrefixList += "|";
}// REMIND: decide whether to allow the "null" class prefix
// or not.
packagePrefixList += "sun.net.www.protocol";
StringTokenizer packagePrefixIter =
new StringTokenizer(packagePrefixList, "|");
while (handler == null &&
packagePrefixIter.hasMoreTokens()) {String packagePrefix =
packagePrefixIter.nextToken().trim();
try {
String clsName = packagePrefix + "." + protocol +
".Handler";
Class> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
handler=
(URLStreamHandler)cls.newInstance();
}
} catch (Exception e) {
// any number of exceptions can get thrown here
}
}
}synchronized (streamHandlerLock) {URLStreamHandler handler2 = null;
// Check again with hashtable just in case another
// thread created a handler since we last checked
handler2 = handlers.get(protocol);
if (handler2 != null) {
return handler2;
}// Check with factory if another thread set a
// factory since our last check
if (!checkedWithFactory && factory != null) {
handler2 = factory.createURLStreamHandler(protocol);
}if (handler2 != null) {
// The handler from the factory must be given more
// importance. Discard the default handler that
// this thread created.
handler = handler2;
}// Insert this handler into the hashtable
if (handler != null) {
handlers.put(protocol, handler);
}}
} return handler;
}
代码段
if (factory != null) {
handler = factory.createURLStreamHandler(protocol);
checkedWithFactory = true;
}
这一段是从factory中获取相应协议的URLStreamHandler,如果获取不到,则从另一渠道获得(即不会对java已经支持的协议造成影响),就是我们讲的第2种方法
各个构造方法的调用关系代码如下:
//#1
public URL(String spec) throws MalformedURLException {
this(null, spec);
//调用#2 URL(URL context, String spec)
}
//#2
public URL(URL context, String spec) throws MalformedURLException {
this(context, spec, null);
调用#6 URL(URL context, String spec, URLStreamHandler handler)
}//#3
public URL(String protocol, String host, int port, String file)
throws MalformedURLException
{
this(protocol, host, port, file, null);
//调用#6 RL(String protocol, String host, int port, String file,URLStreamHandler handler)
}//#4
public URL(String protocol, String host, String file)
throws MalformedURLException {
this(protocol, host, -1, file);
//调用#3 URL(String protocol, String host, int port, String file)
}//#5
public URL(String protocol, String host, int port, String file,
URLStreamHandler handler)throws MalformedURLException{
//....
}//#6
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException{
//....
}//可以看出,实质性逻辑都在#5和#6方法
2、 通过 JVM 启动参数 -Djava.protocol.handler.pkgs来设置 URLStreamHandler 实现类的包路径比如-D java.protocol.handler.pkgs=com.myprotocol.pkgs0|com.myprotocol.pkgs1,多个用|分割,java默认的包为sun.net.www.protocol,设置了这个参数,会拼接在默认的包之后,即sun.net.www.protocol|com.myprotocol.pkgs0|com.myprotocol.pkgs1 看这段代码
if (handler == null) {
String packagePrefixList = null;
packagePrefixList
= java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
protocolPathProp,""));
//protocolPathProp的值为java.protocol.handler.pkgs if (packagePrefixList != "") {
packagePrefixList += "|";
} // REMIND: decide whether to allow the "null" class prefix
// or not.
packagePrefixList += "sun.net.www.protocol";
//拼接默认的pkgs StringTokenizer packagePrefixIter =
new StringTokenizer(packagePrefixList, "|");
while (handler == null &&
packagePrefixIter.hasMoreTokens()) {//遍历pkgsString packagePrefix =
packagePrefixIter.nextToken().trim();
try {
String clsName = packagePrefix + "." + protocol +
".Handler";
//类全名为pkgs.protocal.Handler
Class> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
handler=
(URLStreamHandler)cls.newInstance();
}
} catch (Exception e) {
// any number of exceptions can get thrown here
}
}
}
类的命名模式为 [pkgs].[protocol].Handler,比如默认实现” sun.net.www.protocol.[protocol].Handler”, 比如HTTP 协议的对应的处理类名为 -sun.net. www.protocol.http.Handler
自定义协议例子如下:
package com.myprotocol.pkgs0.json;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
public class Handler extends URLStreamHandler { @Override
protected URLConnection openConnection(URL u) throws IOException {return new URLConnection(u) {public InputStream getInputStream() throws IOException {
return new FileInputStream("d:/aaaa.txt");
}@Override
public void connect() throws IOException {
// 建立连接
}
};
}
}
启动时命令:java -Djava.protocol.handler.pkgs=com.myprotocol.pkgs0 其他参数 主类
3、构造方法URL((URL)null, "json://www.google.com",new URLStreamHandler(){...}) 这种方法直接设置Handler,比较简单,不在赘述
代理Proxy
URLStreamHandler 覆盖openConnection(URL) 和openConnection(URL,Proxy) 两个方法即可
【对Java的URL类支持的协议进行扩展的方法】
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)