前面的一篇文章《Java中的Internet查詢》分析完了如何經過IP地址或者主機名肯定主機在因特網中的地址。任意給定主機上可能會有任意多個資源,這些資源須要有標識符方便主機之間訪問對方的資源,所以這篇文章深刻分析一下URL和URI。html
URI全稱是Uniform Resource Identifier,也就是統一資源標識符,它是一種採用特定的語法標識一個資源的字符串表示。URI所標識的資源多是服務器上的一個文件,也多是一個郵件地址、圖書、主機名等。簡單記爲:URI是標識一個資源的字符串(這裏先沒必要糾結標識的目標資源究竟是什麼,由於使用者通常不會見到資源的實體),從服務器接收到的只是資源的一種字節表示(二進制序列,從網絡流中讀取)。URI的語法構成是:一個模式和一個模式特定部分。表示形式以下:java
模式:模式特定部分 scheme:scheme specific part
注意:模式特定部分的表示形式取決於所使用的模式。URI當前的經常使用模式包括:git
除此以外,Java中還大量使用了一些非標準的定製模式,如rmi、jar、jndi、doc、jdbc等,這些非標準的模式分別用於實現各類不一樣的用途。github
URI中的模式特定部分沒有固定的語法,不過,不少時候都是採用一種層次結構形式,如:apache
//受權機構/路徑?查詢參數 //authority/path?query
URI的authority部分指定了負責解析該URI其餘部分的受權機構(authority),不少時候,URI都是使用Internet主機做爲受權機構。例如http://www.baidu.com/s?ie=utf-8,受權機構是www.baidu.com(在URL角度來看,主機名是www.baidu.com)。編程
URI的路徑(path)是受權機構用來肯定所標識資源的字符串。不一樣的受權機構可能會把相同的路徑解析後指向不一樣的資源。其實這一點很明顯,試下你寫兩個不一樣的項目,主頁的路徑都是/index.html,它們必定是徹底相同的html文檔。另外,路徑是能夠分層的,分層的各個部分使用斜線"/"進行分隔,而"."和".."操做符用於在分層層次結構中的導航(後面這兩個操做符可能不多見到,瞭解便可)。數組
URI的模式的組成部分能夠是小寫字母、數字、加號、點(.)和連號(-)。典型的URI的其餘三個部分(模式特定部分,也就是受權機構、路徑和查詢參數)分別由ASCII字母組成(也就是字母A-Z、a-z和數字0-9),此外,還可使用標點符號-、_、.、!和~
,而定界符(如/、?、&和=)有其預約義的用途。全部的其餘字符,包括ASCII中拉丁字母,都須要使用百分號(%)轉義,轉義後的格式是:%+字符按照UTF-8編碼再轉成16進制的字符串表示。注意一點,若是前面提到的定界符沒有做爲定界符使用,也須要進行轉義。舉個簡單的例子,如URI中存在中文字符"木",木字的UTF-8編碼爲0xE6 0x9C 0xA8,那麼它在URI中應該轉義爲%E6%9C%A8。Jdk中的URLEncoder或者apache-codec中的相關類庫提供URI(URL)編碼的功能。瀏覽器
URI中還能夠攜帶用戶的口令,由於會有安全漏洞,因此並不常見,這裏也不展開分析。緩存
URI在Java中抽象爲java.net.URI類。安全
URI實例構造方法有不少個:
public URI(String str) throws URISyntaxException public URI(String scheme,String userInfo, String host, int port, String path, String query, String fragment) throws URISyntaxException public URI(String scheme, String authority, String path, String query, String fragment)throws URISyntaxException public URI(String scheme, String host, String path, String fragment) throws URISyntaxException public URI(String scheme, String ssp, String fragment) throws URISyntaxException //靜態工廠方法,最終調用的是URI(String str) public static URI create(String str)
注意的是構造URI實例的時候會檢查是否符合URI的語法,不然會拋出URISyntaxException異常。以上的全部方法都是基於URI的模式+模式特定部分或者URI的各個部分構造URI實例,而靜態工廠方法public static URI create(String str)
主要是屏蔽了非檢查型異常URISyntaxException,轉化爲檢查型異常IllegalArgumentException,這樣就不須要顯式捕獲異常。
前面也提到,URI引用包括最多三個部分:模式、模式特定部分和片斷標識符,通常格式爲:
模式:模式特定片斷:片斷標識符
基於這種語法規範,URI類提供了下面幾個方法獲取這些屬性:
public String getScheme() public String getRawSchemeSpecificPart() public String getSchemeSpecificPart() public String getFragment() public String getRawFragment()
PS:之因此沒有getRawScheme()
方法,是由於URI規範中強制規定,全部的模式名稱必須由URI規範中合法的ASCII字符組成,也就是模式名稱中不容許存在百分號轉義。上面的getRawSchemeSpecificPart()
是返回原始的模式特定部分,getSchemeSpecificPart()
返回了通過解碼(decode)的模式特定部分。同理,getRawFragment()
返回原始的片斷標識符,而getFragment()
返回通過解碼(decode)的片斷標識符。固然,還有其餘方法獲取URI的基礎屬性:
//是否絕對URI public boolean isAbsolute() //是否不透明的URI,若是isOpaque()返回true,URI是不透明的 //只能獲取到模式、模式特定部分和片斷標識符,獲取不了host、port等 public boolean isOpaque() public String getAuthority() public String getRawAuthority() public String getRawUserInfo() public String getUserInfo() public String getHost() public int getPort() public String getRawPath() public String getPath() public String getRawQuery() public String getQuery()
PS:isOpaque()
方法爲true的時候,說明URI是不透明的,不透明的URI沒法獲取受權機構、路徑、端口和查詢參數等。另外,上面的獲取屬性的方法有些方法存在getRawFoo()
的對應方法,這些getRawFoo()
方法就是獲取原始屬性的值,若是沒有Raw
關鍵字,則返回解碼後的字符串值。
URI中提供了三個方法用於絕對URI和相對URI之間的轉換:
public URI resolve(URI uri) public URI resolve(String str) public URI relativize(URI uri)
其中,resolve
方法是基於絕對URI和相對URI把相對URI補全爲絕對URI,例如:
public static void main(String[] args) throws Exception{ URI absolute = URI.create("http://localhost:8080/index.html"); URI relative = URI.create("/hello.html"); URI resolve = absolute.resolve(relative); System.out.println(resolve); } //控制檯輸出 http://localhost:8080/hello.html
而relativize
方法是基於絕對URI和相對URI反解出絕對URI中的相對URI部分,例如:
public static void main(String[] args) throws Exception{ URI absolute = URI.create("http://localhost:8080/index.html"); URI relative = URI.create("http://localhost:8080/"); URI resolve = relative.relativize(absolute); System.out.println(resolve); } //控制檯輸出 index.html
URI類實現了Comparable接口,所以URI能夠排序。URI的相等性不是基於字符串直接比較,相等的URI在透明性上必須是一致的,例如都是不透明的,其餘部分才能夠進行比較。URI比較的時候,模式和受權機構是忽略大小寫的,其餘部分必須區分大小寫,用於轉義無效字符串的十六進制數字除外。須要轉義的字符在轉義前和轉義以後進行比較,會認爲是不一樣的URI。
//1. URI uri1 = URI.create("http://localhost:8000/index.html"); URI uri2 = URI.create("http://LOCALHOST:8000/index.html"); System.out.println(uri1.equals(uri2)); //輸出:true //2. URI uri3 = URI.create("http://localhost:8000/index/A"); URI uri4 = URI.create("http://LOCALHOST:8000/index/%41"); System.out.println(uri3.equals(uri4)); //輸出:false
URI中有兩個方法返回其字符串表示:
public String toString() public String toASCIIString()
toString()
方法返回未編碼的字符串形式,也就是特殊的字符不使用百分號轉義,所以這個方法的返回值不能保證符合URI的語法,儘管它的各個部分是遵循URI的語法規範的。toASCIIString()
方法返回通過編碼的字符串形式(US-ACSII編碼),也就是特殊的字符必定通過了百分號轉義。toString()
存在的意義是提升URI的可讀性,toASCIIString()
方法存在的意義是提升URI的可用性。
URL全稱是Uniform Resource Location,也就是統一資源位置。實際上,URL就是一種特殊的URI,它除了標識一個資源,還會爲資源提供一個特定的網絡位置,客戶端能夠經過它來獲取URL對應的資源。
URL所表示的網絡資源位置一般包括用於訪問服務器的協議(如http、ftp等)、服務器的主機名或者IP地址、以及資源文件在該服務器上的路徑。典型的URL例如http://localhost/myProject/index.html,它指示本地服務器的myProject目錄下有一個名爲index.html的文檔,這個文檔能夠經過http協議訪問(實際上,URL不必定是指服務器中的真實的物理路徑,由於咱們通常在服務器中部署應用,如Servlet應用,URL訪問的極可能是應用的接口,至於最終映射到什麼資源能夠由應用自身決定)。
URL的語法表示形式爲:
protocol://userInfo@host:port/path?query#fragment 協議://用戶信息@主機名:端口/路徑?查詢#片斷
protocol:URL中的協議(protocol)是相應於URI中的模式(schema)的另外一個叫法。URL中,協議部分能夠是file、ftp、http、https、magnet、telnet或者其餘定製協議字符串(可是不包括urn)。
userInfo:URL中的用戶信息(userInfo)是服務器的登陸信息,這部分信息是可選的。若是這部分信息存在,通常會包含一個用戶名,極少狀況下會包含一個口令。實際上URL攜帶用戶信息是不安全的。
port:URL中的端口號(port)是指服務器中應用的運行端口,默認端口爲80,此部分信息是可選的(也就若是不指定端口號就使用默認端口80)。
path:URL中的路徑(path)用於表示服務器上的一個特定的目錄(其實說一個特定的文件也能夠),這個特定的目錄不必定是物理目錄,也有多是邏輯目錄。這一點很容易說明,通常不可能把服務器上面的目錄直接公開讓全部人訪問,服務器上面跑的通常是Web(Java的話通常是Servlet)應用,路徑指向的實際數據來源甚至很大多是在其餘服務器上的MySQL中的查詢結果。
query:查詢參數(query)通常是一個字符串,它表示URL中向服務器提供的附加參數,通常只使用在http協議的URL中,其中包含了表單數據,來源於用戶的輸入,表示形式是key1=value1&key2=value2&keyn=valuen。
fragment:片斷(fragment)表示遠程服務器資源的某個特定的部分。假如服務器資源是一個HTML文檔,此片斷標識符將制定爲該HTML文檔的一個錨(Anchor)。若是遠程資源是一個XML文檔,那麼這個片斷標識符是一個XPointer。(若是用Markdown寫過博客就知道,添加了導航目錄以後,片斷就是目錄將要導航到的目的章節)
URL能夠告知瀏覽器一個文檔(Document,假設URL對應服務器上的資源統一叫作文檔)的大量信息:獲取文檔所使用的協議、文檔所在的主機、文檔在該主機中的路徑等。文檔中可能也存在引用和當前URL相同的URL,所以,在該文檔中使用URL的時候並不要求完整地指定每個URL,URL能夠繼承其父文檔的協議、主機名和路徑。繼承了父文檔URL的部分信息的這類不完整的URL稱爲相對URL(Reletive URL),相反,完整指定全部部分的URL稱爲絕對URL(Absolute URL)。在相對URL中,缺乏的各個部分與請求該文檔的URL對應的部分相同。舉個例子,咱們訪問本地服務器的一個HTML文檔,URL爲http://localhost:8080/myProject/index.html,index.html文檔中存在一個超連接<a href="login.html">
,當咱們點擊此超連接的時候,瀏覽器會從原始URL(http://localhost:8080/myProject/index.html)中截去index.html而後拼接login.html,最後訪問http://localhost:8080/myProject/login.html。
若是相對URL以"/"開頭,那麼它是相對於文檔的根目錄,而不是當前的文檔。舉個例子,咱們訪問本地服務器的一個HTML文檔,URL爲http://localhost:8080/myProject/index.html,index.html文檔中存在一個超連接<a href="/login.html">
,當咱們點擊此超連接的時候,瀏覽器會跳轉到http://localhost:8080/login.html。
相對URL有兩個顯著的優勢:
java.net.URL類(後面直接叫URL)是JDK對URL的統一抽象,它是一個final修飾的類,也就是不容許派生子類。實際上,URL設計的時候採用了策略模式,不一樣的協議的處理器就是不一樣的策略,而URL類構成了上下文,經過它決定選用何種策略。URL的核心屬性包括協議(或者叫模式)、主機名、端口、查詢參數字符串和片斷標識符(在JDK中被命名爲ref)等,每一個屬性均可以單獨設置。一旦一個URL對象被構造以後,它的全部屬性都不能改變,也就是它的實例是線程安全的。
URL實例的主要構造方法以下:
//基於URL的各個部分構造URL實例,其中file至關於path、query和fragment三個部分組成 public URL(String protocol, String host, int port, String file) throws MalformedURLException //基於URL的各個部分構造URL實例,其中file至關於path、query和fragment三個部分組成,使用默認端口80 public URL(String protocol, String host, String file) throws MalformedURLException //基於URL模式構造URL實例 public URL(String spec) throws MalformedURLException //基於上下文(父)URL和URL模式構造相對URL實例 public URL(URL context, String spec) throws MalformedURLException
下面基於上面幾個構造URL實例的方法舉幾個簡單的編碼例子:
//1. //注意file要帶斜杆前綴/ URL url = new URL("http", "127.0.0.1", 8080, "/index"); //輸出http://127.0.0.1:8080/index System.out.println(url.toString()); //2. URL url = new URL("http://127.0.0.1:8080/index"); //輸出http://127.0.0.1:8080/index System.out.println(url.toString()); //3. URL url = new URL("http", "127.0.0.1", "/index"); //輸出http://127.0.0.1/index System.out.println(url.toString()); //4. URL context = new URL("http", "127.0.0.1", "/index"); //構造相對URL,保留協議、host、port部分 URL url = new URL(context, "/login"); //輸出http://127.0.0.1/login System.out.println(url);
上面只說到經過URL類去構造對象,其實還有其餘方法獲取URL實例,例如:
URL systemResource = ClassLoader.getSystemResource(String name) Enumeration<URL> systemResources = ClassLoader.getSystemResources(String name) URL resource = UrlMain.class.getResource(String name) URL resource = UrlMain.class.getClassLoader().getResource(String name) Enumeration<URL> resources = UrlMain.class.getClassLoader().getResources(String name)
其中,ClassLoader.getSystemResource(String name)
和ClassLoader.getSystemResources(String name)
都是先判斷是否存在SystemClassLoader,若存在則使用SystemClassLoader加載資源,不然使用BootstrapClassLoader(BootstrapClassPath)進行資源加載,簡單來講,這兩個方法就是使用系統類加載器加載資源,加載資源時候,從類路徑中加載資源,例如使用IDEA,則從編譯後的/target目錄中加載資源,這一點可使用ClassLoader.getSystemResource("")
進行驗證。其餘三個方法Class#getResource(String name)
、Class#getClassLoader()#getResource(String name)
和Class#getClassLoader()#getResources(String name)
本質上都是基於AppClassLoader進行資源加載,它們加載資源的時候是從當前的Class所在的類路徑(包括類的包路徑,若是使用IDEA通常也是/target/類所在的包目錄)進行加載,例若有一個類club.throwable.Main.class
,若是目錄club.throwable存在一張圖片doge.jpg,則加載圖片的時候能夠這樣子club.throwable.Main.class.getResource("doge.jpg")
。值得注意的一點是,若是須要加載的資源是在一個特定目錄中,那麼Class#getResource(String name)
中的name必須以文件路徑分隔符開頭,例如Window系統中用'/',其餘兩個基於經過ClassLoader實例直接加載的不須要使用文件路徑分隔符開頭,這一點能夠看Class#getResource(String name)
方法中的resolveName(name)
方法。
URL實例提供幾個方法用於獲取數據:
public final InputStream openStream() throws java.io.IOException public URLConnection openConnection() throws java.io.IOException public URLConnection openConnection(Proxy proxy) throws java.io.IOException public final Object getContent() throws java.io.IOException public final Object getContent(Class[] classes) throws java.io.IOException
InputStream openStream()
openStream()
方法鏈接到URL所引用的資源,在客戶端和服務器之間完成必要的握手以後,返回一個InputStream實例,用於讀取網絡流數據。此方法返回的InputStream實例讀取到的內容是Http請求體的原始報文(若是使用Http協議的話),所以有多是一個原始文本片斷或者是一個二進制的序列(例如圖片)。舉個例子:
public static void main(String[] args) throws Exception{ URL url = new URL("https://www.baidu.com"); InputStream inputStream = url.openStream(); int c; while ((c= inputStream.read()) != -1){ System.out.print(c); } inputStream.close(); }
URLConnection openConnection()
openConnection()
和openConnection(Proxy proxy)
是類似的,只是後者可使用代理。openConnection()
方法爲指定的URL新建一個socket,而且返回一個URLConnection實例,它表示一個網絡資源打開的鏈接,咱們能夠從這個打開的鏈接獲取一個InputStream實例,用於讀取網絡流數據。若是上面的過程出現調用失敗,會拋出一個IOException。
Object getContent()
Object getContent()
和Object getContent(Class[] classes)
是類似的,後者能夠經過Class數組指定獲取到的URL中的請求體內容轉化爲對應的類型的實體。Object getContent()
其實是調用了URLConnection實例中的getContent方法,通常來講,不建議使用這兩個方法,由於轉換的邏輯執行後的結果通常是不合符咱們預想的結果。
URL的屬性獲取能夠理解爲分解URL成URL組成的各個部分,這些部分的信息能夠單獨獲取。前面提到過URL的各個組成部分,這裏重複一下,URL分別由下面的幾個部分組成:
URL類中提供對應的方法分別是:
//獲取模式(協議) public String getProtocol() //獲取主機名 public String getHost() //獲取受權機構,通常是host:port的形式 public String getAuthority() //獲取端口號port public int getPort() //返回協議的默認端口,如http協議的默認端口號爲80,若是沒有指定協議的默認端口則返回-1 public int getDefaultPort() //返回URL字符串中從主機名後的第一個斜杆/一直到片斷標識符的#字符以前的全部字符 public String getFile() //返回的值和getFile()類似,可是不包含查詢字符串 public String getPath() //返回URL的片斷標識符部分 public String getRef() //返回URL的查詢字符串 public String getQuery() //返回URL中的用戶信息,不經常使用 public String getUserInfo()
其中須要注意的是getFile()
和getPath()
,舉個例子:
URL url = new URL("https://localhost:8080/search?name=doge#anchor-1"); System.out.println(String.format("path=%s", url.getPath())); System.out.println(String.format("file=%s", url.getFile())); //控制檯輸出 path=/search file=/search?name=doge
URL實例的比較一般是使用equals()
和hashCode()
方法,當且僅當兩個URL指向相同的主機、端口和路徑上的資源,而且二者的片斷標識符和查詢字符串都相同的時候,纔會認爲兩個URL是相等的。equals()
調用的時候會嘗試使用DNS解析主機,此方法有多是一個阻塞的IO操做,會形成比較大的性能消耗,這個時候須要考慮使用緩存,或者把URL轉化爲URI進行比較。
URL類中有三個經常使用的實例方法用於轉換爲另外一種形式:
public String toString() public String toExternalForm() public URI toURI()
實際上,toString()
最終調用的是toExternalForm()
,而toExternalForm()
方法是使用StringBuilder把URL的各個組成部分拼接,返回的字符串能夠方便直接使用在瀏覽器中。toURI()
方法就是把URL實例轉化爲URI實例。
咱們在作Web項目或者使用POSTMAN的時候常常會看到x-www-form-urlencoded,它是經常使用的媒體類型或者說內容類型(ContentType),使用這種類型的時候,URL中的字符須要進行編碼,那麼爲何須要進行編碼呢?這個是歷史遺留緣由,由於發明Web或者說Http(s)協議的時候,Unicode編碼並未徹底普及,URL中使用的字符必須來自ASCII的一個固定子集,這個固定子集是:
-_.!~*'(和,)
。其餘字符如:/&?@#;$+=%
也可使用,可是它們限定於使用在特定的用途,若是這些字符串出如今URL路徑或者查詢字符串,它們以及路徑和查詢字符串的內容必須進行編碼。
這裏有個須要注意的地方:URL編碼只針對URL路徑和URL路徑以後的部分,由於URL規範中規定,路徑以前的部分必須知足ASCII固定子集的內容。
URL編碼的方法很簡單:除了ASCII數字、字母和部分指定的標點符號以外,全部的其餘字符都要轉化爲字節表示,每一個字節要轉化爲百分號(%)後面加2位十六進制數字。空格是一種特殊的字符,它使用得比較廣泛,通常空格能夠編碼爲%20或者加號(+),可是加號自己的編碼爲%2B。而/#=&和?
不做爲分隔符的時候,必須進行編碼。
解碼過程就是上面編碼過程的逆向操做,這裏不具體展開。
Java中提供兩個類java.net.URLEncoder和java.net.URLDecoder分別用於URL的編碼和解碼,注意須要使用兩個參數的靜態方法encode(String value,String charset)
和decode(String value,String charset)
,單參數的方法已通過期,不建議使用。注意在使用java.net.URLEncoder和java.net.URLDecoder的時候,它們的API不會判斷URL的什麼部分須要編碼和解碼,什麼部分不須要編碼和解碼,直接整個URL字符串丟進去編碼必定會出現意料以外的結果。舉個反面列子:
String url = "http://localhost:9090/index?name=張大doge"; String encode = URLEncoder.encode(url, "UTF-8"); System.out.println(encode); //輸出:http%3A%2F%2Flocalhost%3A9090%2Findex%3Fname%3D%E5%BC%A0%E5%A4%A7doge
實際上,咱們只須要對Path和Path以後的字符進行編碼和解碼,例如對於URLhttp://localhost:9090/index?name=張大doge
,咱們須要編碼和解碼的部分只有index、name和張大doge這三個部分,其餘部分應該保持原樣。正確的例子以下:
public static void main(String[] args) throws Exception { String raw= "http://localhost:9090/index?name=張大doge"; String base = raw.substring(raw.lastIndexOf("//")); String pathLeft = base.substring(base.lastIndexOf("/") + 1); String[] array = pathLeft.split("\\?"); String path = array[0]; String query = array[1]; base = raw.substring(0,raw.lastIndexOf(path)); path = URLEncoder.encode(path, "UTF-8"); String[] queryResult = query.split("="); String queryKey = URLEncoder.encode(queryResult[0], "UTF-8"); String queryValue = URLEncoder.encode(queryResult[1], "UTF-8"); System.out.println(base + path + "?" + queryKey + "=" + queryValue); } //輸出結果:http://localhost:9090/index?name=%E5%BC%A0%E5%A4%A7doge //其中UTF-8編碼中張的十六進制表示爲E5 BC A0,大的十六進制編碼爲E5 A4 A7
許多系統會經過代理服務器訪問Web應用或者其餘服務器中的資源,代理服務器接收從本地客戶端發出的請求再轉發請求到遠程服務器,而後遠程服務器返回請求的結果到代理服務器,代理服務器接收到結果以後會把結果回傳到本地服務器。這樣作有兩個比較重要的緣由:
在Java中除了TCP鏈接使用傳輸層的SOCKET代理,其餘應用層代理都不支持。Java對於SOCKET沒有提供禁用代理的選項,可是能夠經過下面三個系統屬性配置來開啓和限制代理:
http.proxyHost:代理服務器的主機名,默認不設置此係統屬性。 http.proxyPort:代理服務器的端口號,默認不設置此係統屬性。 http.noneProxyHosts:不須要代理訪問的主機名,多個用豎線|分隔,默認不設置此係統屬性。 舉個例子: System.setProperty("http.proxyHost", "localhost"); System.setProperty("http.proxyPort", 1080); System.setProperty("http.noneProxyHosts", "www.baidu.com|github.com");
java.net.Proxy類提供了對代理服務器更細粒度的控制,也就是說這個類容許在編程的使用使用不一樣的遠程服務器做爲代理服務器,而不是經過系統屬性全局配置代理。Proxy目前支持三種代理類型,分別是:
使用的時候以下:
SocketAddress socketAddress = new InetSocketAddress("localhost", 80); Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress); Socket socket = new Socket(proxy); //...
每一個運行中的Java虛擬機都會存在一個java.net.ProxySelector
實例對象,用來肯定不一樣鏈接使用的代理服務器。默認的java.net.ProxySelector
的實現是sun.net.spi.DefaultProxySelector
的實例,它會檢查各類系統屬性和URL的協議,再決定若是鏈接到不一樣的遠程代理服務器,固然,開發者也能夠繼承和實現自定義的java.net.ProxySelector
,從而能夠根據協議、主機、路徑日期等其餘標準來選擇不一樣的代理服務器。java.net.ProxySelector
的幾個核心的抽象方法以下:
//獲取默認的ProxySelector實例 public static ProxySelector getDefault() //設置默認的ProxySelector實例 public static void setDefault(ProxySelector ps) //經過URI獲取可用的代理列表 public abstract List<Proxy> select(URI uri) //告知ProxySelector不可用的代理和出現的異常 public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe)
若是須要擴展的話,最好加入緩存的功能,緩存可用的Proxy列表,一旦出現Proxy不可用,經過connectFailed
進行清理和剔除不可用的代理節點便可。
URL和URI是當前的網絡世界或者系統中資源的重要標識符,瞭解它們的基礎知識才能更好地進行網絡編程。URI是統一資源標識符,它能夠表示任何介質中的資源。而URL是統一資源位置,通常特指因特網中的網絡資源位置,使用於Http或者Https協議中。很顯然,URI能夠表示的範圍更大,URI其實是包含了URL。而二者的區別能夠參看上面的幾個小節。
(c-5-d e-20181003)