日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

URL 源码分析

發(fā)布時間:2024/9/30 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 URL 源码分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

需要了解的知識點(diǎn):
URI、 URL 和 URN 的區(qū)別
URI 源碼分析

URL 和URI的最大區(qū)別是:
URL可以定位到一個資源,也就是說,URL類可以訪問URL指定的資源信息。
URI只是標(biāo)識一個對象,所以URI類無法獲取URI標(biāo)識的對象。

下面通過源碼來分析URL類的實(shí)現(xiàn)細(xì)節(jié):

構(gòu)造

public URL(String spec); public URL(String protocol, String host, String file); public URL(String protocol, String host, int port, String file) public URL(String protocol, String host, int port, String file,URLStreamHandler handler); public URL(URL context, String spec); public URL(URL context, String spec, URLStreamHandler handler);

URL提供了6種不同的構(gòu)造方法,使用那個構(gòu)造方法取決你有那些信息以及信息形式。
1. 根據(jù)一個字符串形式的URL,來構(gòu)建URL對象。
2. 根據(jù) 協(xié)議、主機(jī)名、文件來構(gòu)造一個URL。
使用該協(xié)議默認(rèn)的端口,并且file參數(shù)應(yīng)當(dāng)以斜線開頭,包括文件路徑、文件名稱和片段。
3. 根據(jù) 協(xié)議、主機(jī)名、端口、文件來構(gòu)造一個URL。
4. 根據(jù) 協(xié)議、主機(jī)名、端口、文件和URLStreamHandler來構(gòu)造一個URL。
URLStreamHandler:主要是用來讀取指定的資源,并返回該資源的一個流。
5. 根據(jù)一個基礎(chǔ)URL和一個相對URL來構(gòu)建一個絕對URL。
6. 根據(jù)一個基礎(chǔ)URL和一個相對URL來構(gòu)建一個絕對URL,并傳入一個URLStreamHandler對象。

解析URL

URL主要是通過7部分組成,如下圖:

  • 獲得URL的協(xié)議
    public String getProtocol()

  • 獲得授權(quán)機(jī)構(gòu)信息(包括用戶信息、主機(jī)和端口)
    public String getAuthority()

  • 獲得用戶信息(用戶名和密碼)
    public String getUserInfo()

  • 獲取主機(jī)地址(域名或ip地址)
    public String getHost()

  • 獲得端口
    public int getPort()

  • 獲得文件信息(路徑、文件名和查詢參數(shù))
    public String getFile()

  • 獲得路徑信息(路徑、文件名)
    public String getPath()

  • 獲取查詢參數(shù)信息
    public String getQuery()

  • 獲得片段信息
    public String getRef()

  • 獲得該協(xié)議默認(rèn)端口
    public int getDefaultPort()

  • 解析URL 示例

    URL url = new URL("http://user:pass@localhost:8080/infcn/index.html?type=type1#aaa"); System.out.println("protocol :\t"+url.getProtocol()); System.out.println("authority :\t"+url.getAuthority()); System.out.println("userinfo :\t"+url.getUserInfo()); System.out.println("host :\t"+url.getHost()); System.out.println("port :\t"+url.getPort()); System.out.println("file :\t"+url.getFile()); System.out.println("path :\t"+url.getPath()); System.out.println("query :\t"+url.getQuery()); System.out.println("ref :\t"+url.getRef()); System.out.println("defaultport:\t"+url.getDefaultPort());

    URL 獲取數(shù)據(jù)

    從概念上區(qū)分:URI只是標(biāo)識一個資源,而URL可以定位一個資源。
    所以java總URI只負(fù)責(zé)解析URI功能,而URL有解析URL的功能,還有獲取URL指定資源的數(shù)據(jù)。

    可以通過以下5個方法來獲取URL指定的資源數(shù)據(jù)
    public URLConnection openConnection();
    public URLConnection openConnection(Proxy proxy);
    public final InputStream openStream();
    public final Object getContent();
    public final Object getContent(Class[] classes);

    openConnection()方法

    public URLConnection openConnection() throws java.io.IOException {return handler.openConnection(this); }

    直接調(diào)用URLStreamHandler.openConnection()方法獲取URLConnection對象。URLConnection 對象可以獲取原始的文檔(如:html、純文本、二進(jìn)制圖像等),還可以獲取訪問這個協(xié)議指定的所有的元數(shù)據(jù)(如:http協(xié)議的請求頭信息)。URLConnection 對象除了從URL中讀取資源外,還允許向URL中寫入數(shù)據(jù)。(如:http post提交表單數(shù)據(jù),mailto 發(fā)送電子郵件等)

    URLStreamHandler可以讓系統(tǒng)根據(jù)當(dāng)前URL協(xié)議來選擇響應(yīng)的Handler,也可以使用擴(kuò)展URLStreamHandler類來自定義實(shí)現(xiàn)相應(yīng)資源的獲取,也可以擴(kuò)展協(xié)議。

    URLStreamHandler 類結(jié)構(gòu)

    public abstract class URLStreamHandler{abstract protected URLConnection openConnection(URL u) throws IOException;protected URLConnection openConnection(URL u, Proxy p) throws IOException {throw new UnsupportedOperationException("Method not implemented.");}protected void parseURL(URL u, String spec, int start, int limit){...}protected int getDefaultPort() {return -1;}protected boolean equals(URL u1, URL u2) {String ref1 = u1.getRef();String ref2 = u2.getRef();return (ref1 == ref2 || (ref1 != null && ref1.equals(ref2))) &&sameFile(u1, u2);}protected int hashCode(URL u){...}protected boolean sameFile(URL u1, URL u2) {// Compare the protocols.if (!((u1.getProtocol() == u2.getProtocol()) ||(u1.getProtocol() != null &&u1.getProtocol().equalsIgnoreCase(u2.getProtocol()))))return false;// Compare the files.if (!(u1.getFile() == u2.getFile() ||(u1.getFile() != null && u1.getFile().equals(u2.getFile()))))return false;// Compare the ports.int port1, port2;port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort();port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort();if (port1 != port2)return false;// Compare the hosts.if (!hostsEqual(u1, u2))return false;return true;}...... }

    由此類可以看出,子類必須覆蓋的方法是openConnection(URL u)方法,如果該協(xié)議支持代理模式,則也需要覆蓋openConnection(URL u, Proxy p)方法。

    URLStreamHandler 實(shí)例化

    如果用戶傳過來的URLStreamHandler 實(shí)例,則需要驗(yàn)證安全性問題。比如:applet程序是在客戶的瀏覽器端運(yùn)行的java程序,他如果讀取服務(wù)器上jar文件的時候就可以通過,如果讀取客戶端本地磁盤中的文件則不允許訪問。代碼如下圖:

    如果用戶沒有指定URLStreamHandler實(shí)例,則通過protocol協(xié)議來決定使用哪個協(xié)議的URLStreamHandler的實(shí)例。代碼如下:

    jdk的sun.net.www.protocol包中默認(rèn)支持以下幾種協(xié)議,當(dāng)然用戶也可以擴(kuò)展URLStreamHandler實(shí)例,來實(shí)現(xiàn)自定義的協(xié)議。
    java中默認(rèn)支持的協(xié)議如下圖:

    openConnection(Proxy proxy) 方法

    public URLConnection openConnection(Proxy proxy) throws java.io.IOException {if (proxy == null) {throw new IllegalArgumentException("proxy can not be null");}// Create a copy of Proxy as a security measureProxy p = proxy == Proxy.NO_PROXY ? Proxy.NO_PROXY : sun.net.ApplicationProxy.create(proxy);SecurityManager sm = System.getSecurityManager();if (p.type() != Proxy.Type.DIRECT && sm != null) {InetSocketAddress epoint = (InetSocketAddress) p.address();if (epoint.isUnresolved())sm.checkConnect(epoint.getHostName(), epoint.getPort());elsesm.checkConnect(epoint.getAddress().getHostAddress(), epoint.getPort());}return handler.openConnection(this, p); }

    可以通過URL對象設(shè)置的代理來獲取URLConnection對象。

    openStream() 方法

    public final InputStream openStream() throws java.io.IOException {return openConnection().getInputStream(); }

    直接獲取URL指定資源的流InputStream對象,該方法無法向URL中寫如數(shù)據(jù),也無法訪問這個協(xié)議的所有的元數(shù)據(jù)(如:html協(xié)議的請求頭)。

    getContent() 方法

    public final Object getContent() throws java.io.IOException {return openConnection().getContent(); }

    getContent() 方法返回由URL引用的數(shù)據(jù),嘗試由它建立某種類型的對象。如果URL指定的資源是文本類型(如:html、asciii 文件),返回的就是InputStream對象。如果URL指定的資源是圖片則返回java.awt.ImageProducer對象。

    getContent(Class[] classes) 方法

    final Object getContent(Class[] classes) throws java.io.IOException {return openConnection().getContent(classes); }

    該方法允許用戶選擇希望將內(nèi)容作為那個類型返回。
    例如,如果首先將HTML文件作為一個String返回,而第二個選擇是Reader,第三個選擇是InputStream,就可以編寫一下代碼:

    URL u = new URL("http://www.jijianshuai.com"); Class<?> types = new Class[3]; types[0] = String.class; types[1] = Reader.class; types[2] = InputStream.class; Object obj = u.getContent(types);

    equals 方法

    URL類的equals方法是調(diào)用URLStreamHandler對象的equals方法。

    protected boolean equals(URL u1, URL u2) {String ref1 = u1.getRef();String ref2 = u2.getRef();return (ref1 == ref2 || (ref1 != null && ref1.equals(ref2))) &&sameFile(u1, u2);}

    equals方法會對比 URL指向的主機(jī)、端口、文件路徑和片段標(biāo)識。當(dāng)所有的都一樣才會返回true,但equals方法也會嘗試解析DNS,來判斷兩個主機(jī)是否相同。如:可以判斷http://localhost:8080/index.html 和 http://127.0.0.1:8080/index.html 兩個URL是相等的。
    equals方法底層是調(diào)用了sameFile()方法。

    注意:
    因?yàn)閑quals方法有解析DNS的功能,解析DNS是一個阻塞IO操作!所以應(yīng)當(dāng)避免URL存儲在依賴equals()的數(shù)據(jù)結(jié)構(gòu)中,如HashMap。如果要存儲最后是使用URI來進(jìn)行存儲。URI的equals方法是不會解析DNS的。

    sameFile(URL u1, URL u2) 方法

    該方法作用和equals基本相同,這里也包括DNS解析,不過sameFile()不考慮片段標(biāo)識問題。下面通過代碼來解析sameFIle的實(shí)現(xiàn)。

    protected boolean sameFile(URL u1, URL u2) {if (!((u1.getProtocol() == u2.getProtocol()) ||(u1.getProtocol() != null &&u1.getProtocol().equalsIgnoreCase(u2.getProtocol()))))return false;if (!(u1.getFile() == u2.getFile() ||(u1.getFile() != null && u1.getFile().equals(u2.getFile()))))return false;int port1, port2;port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort();port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort();if (port1 != port2)return false;if (!hostsEqual(u1, u2))return false;return true; }
  • 判斷兩個URL的協(xié)議是否一致
  • 判斷兩個URL的獲得文件信息是否一致。文件信息包括:路徑、文件名和查詢參數(shù)
  • 判斷兩個URL的端口是否一致
  • 判斷host是否一致。調(diào)用hostsEqual方法來判斷。
  • hostsEqual(URL u1, URL u2) 方法

    protected boolean hostsEqual(URL u1, URL u2) {InetAddress a1 = getHostAddress(u1);InetAddress a2 = getHostAddress(u2);// if we have internet address for both, compare themif (a1 != null && a2 != null) {return a1.equals(a2);// else, if both have host names, compare them} else if (u1.getHost() != null && u2.getHost() != null)return u1.getHost().equalsIgnoreCase(u2.getHost());elsereturn u1.getHost() == null && u2.getHost() == null; }

    該方法使用兩個URL的host來構(gòu)造InetAddress對象,調(diào)用InetAddress對象的getHost() 方法 來判斷兩個host是否一致。
    InetAddress.getHost()可以通過DNS解析域名,來獲取域名綁定的ip地址。

    本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
    點(diǎn)擊這里快速進(jìn)入簡書

    GIT地址:http://git.oschina.net/brucekankan/
    點(diǎn)擊這里快速進(jìn)入GIT

    總結(jié)

    以上是生活随笔為你收集整理的URL 源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。