注:在此用的JDK8的版本。
首先,URL在java.net
这个包,类签名如下:public final class URL implements java.io.Serializable
可见URL是一个final
类,即URL类无法被继承,并实现了Serializable
接口,即URL对象可被序列化。
再看URL主要的实例属性:
private String protocol;
private String host;
private int port;
private String file;
private transient String query;
private String authority;
private transient String path;
private String ref;
容易发现其实它跟URL格式的每一部分一一对应:scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
再看URL类最常用的一个constructor:public URL(String spec) throws MalformedURLException {
this(null, spec);
}
public URL(URL context, String spec) throws MalformedURLException {
this(context, spec, null);
}
它最终会调到下面这个constructor:public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException
那我们就看这个constructor是怎么实现URL的解析的:
它先把头尾的空格去掉,注意,它这里用的判断是<= ' '
, 因为在ASCII码中小于空格(32)的都是不可见的字符。
然后判断传入的字符串中是否以url:
开头, 如果有,就去掉,所以,如果传入URL字符串是以url:
开头的,JDK还是能解析出来的。
接着判断是否相对URL。
然后解析出protocol,方法比较直观,就是第一个:
前面的所有字符,并把start
变量设置为:
后面第一个为开始。这里还验证了一下protocol的格式,验证比较粗糙,只是确保protocol字符串的首字符是字母,并且protocol字符串全部是字母或者数字且不包含'.','+'和'-'。
然后是根据protocol来加载相应的URLStreamHandler:getURLStreamHandler(protocol)
这个方法里核心的代码如下:
可以看到,这里会根据protocol去相应的包里面去加载Handler
类,比如,如果protocol是HTTP的,就加载sun.net.www.protocol.http.Handler
这个类,并通过反射来new一个实例出来。 接下来就是通过这个Handler来解析和构造这个URL对象了:
这里我们以http协议为例,sun.net.www.protocol.http.Handler
这个类是继承java.net.URLStreamHandler
,parseURL方法调用的就是URLStreamHandler里面实现的:
先解析出spec,这个spec里面除去了Query查询参数部分,只包含Authority,Host,Port和Path部分:接着就是解析出Authority部分,也就是用户和密码:然后在解析出Path:
最后再调用setURL
方法把各部分的值设置好:
setURL
方法最后会调用set
方法如下:
这里值得注意的是file属性,它设置的是:this.file = query == null ? path : path + "?" + query;
就是说如果在URL里面指定了query查询参数,那么file就是path?query
,如果没有,就是path
。我这里为什么要着重强调了这个是因为,这个file属性才是HTTP 请求行中的Path! 如:GET /hello.jsp?name=Eric HTTP/1.1
URL类源码分析到此为止,接下来我会继续分析Java中如何通过一个URL实例来打开一个connection,然后发送请求的整个过程
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this);
}
以下是sun.net.www.protocol.http.Handler的源码
package sun.net.www.protocol.http;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
public class Handler extends URLStreamHandler {
protected String proxy;
protected int proxyPort;
protected int getDefaultPort() {
return 80;
}
public Handler() {
this.proxy = null;
this.proxyPort = -1;
}
public Handler(String var1, int var2) {
this.proxy = var1;
this.proxyPort = var2;
}
protected URLConnection openConnection(URL var1) throws IOException {
return this.openConnection(var1, (Proxy)null);
}
protected URLConnection openConnection(URL var1, Proxy var2) throws IOException {
return new HttpURLConnection(var1, var2, this);
}
}
可以看到 url.openconnection 其实就是 把当前url 作为一个参数 构造 HttpURLConnection 的过程。
其中streamHandler 的作用就是 解析url,构造connection
connection 的作用 和远程服务器交互。