对Java的URL类支持的协议进行扩展的方法

转载自   对Java的URL类支持的协议进行扩展的方法

 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. 
 * <p> 
 * It is used by the {@code URL} class to create a 
 * {@code URLStreamHandler} for a specific protocol. 
 * 
 * @author  Arthur van Hoff 
 * @see     java.net.URL 
 * @see     java.net.URLStreamHandler 
 * @since   JDK1.0 
 */  
public interface URLStreamHandlerFactory {  
    /** 
     * Creates a new {@code URLStreamHandler} instance with the specified 
     * protocol. 
     * 
     * @param   protocol   the protocol ("{@code ftp}", 
     *                     "{@code http}", "{@code nntp}", etc.). 
     * @return  a {@code URLStreamHandler} for the specific protocol. 
     * @see     java.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()) {//遍历pkgs  
  
        String 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.myp rotocol.pkgs0 其他参数 主类

3、构造方法URL((URL)null, "json://www.google.com",new URLStreamHandler(){...})

这种方法直接设置Handler,比较简单,不在赘述


代理Proxy

URLStreamHandler 覆盖openConnection(URL) openConnection(URL,Proxy) 两个方法即可



猜你喜欢

转载自blog.csdn.net/moakun/article/details/80716817