URI 源码分析

需要提前了解下什么是URI,及URI和URL的区别: URI、 URL 和 URN 的区别

URI 引用包括最多三个部分:模式、模式特定部分和片段标识符。一般为: 模式:模式特定部分:片段 如果省略模式,这个URI引用则是相对的。如果省略片段标识符,这个URI引用就是一个纯URI。

URI是对URL的抽象,不仅包括统一资源定位符URL,还包括统一资源名URN。实际上使用的URI大多都是URL。在java中,URI使用java.net.URI类表示,URI类只能标识资源,和解析URI,而不能获取URI所标识的资源(URN是无法定位到资源的)。

构造

public URI(String str) throws URISyntaxException {
    new Parser(str).parse(false);
}

public URI(String scheme, String host, String path, String fragment)
    throws URISyntaxException{
    this(scheme, null, host, -1, path, null, fragment);
}

public URI(String scheme,
           String authority,
           String path, String query, String fragment)
    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 ssp, String fragment) throws URISyntaxException {
    new Parser(toString(scheme, ssp, null, null, null, -1, null, null, fragment)).parse(false);
}

URI类提供了5中构造方法

  1. 根据提供的一个uri字符串构造一个URI对象。
  2. 主要针对层次的URI。通过 模式、服务器地址、文件路径、片段标识构造URI。
  3. 主要针对层次的URI。通过 模式、授权机构、文件路径、查询条件、片段标识构造URI。
  4. 主要针对层次的URI。通过 模式、用户信息、服务器地址、端口、文件路径、查询条件、片段标识构造URI。
  5. 主要针对非层次URI。通过 模式、模式特定部分和片段标识创建URI。

create方法

public static URI create(String str) {
    try {
        return new URI(str);
    } catch (URISyntaxException x) {
        throw new IllegalArgumentException(x.getMessage(), x);
    }
}

如果可以确认URI的格式正确,可使用create的工厂方法创建URI。因为该方法不会抛出URISyntaxException异常。

是否透明URL

URI通常情况下都是层次(带“/”)的,但是也有不透明(没有“/”)的,层次的URI包含模式,主机,站点等各个部分,当然可能某些部分不包含,但是不透明的URI只包含三个部分,Scheme,Scheme-specific-part,Fragment. 如:mailto:jijianshuai@infcn.com.cn

public boolean isOpaque() {
    return path == null;
}

判断path是否为空,如果为空则是不透明的,说明URI中没有“/”。

Paste_Image.png

在URI构造器中解析URI,代码:new Parser(str).parse(false);

判断URI中是否存在“/”符号,如果存在就是有层次结构的URI。 如果存在“/”,则调用parseAuthority方法进行解析path。

URI获取各部分信息

  1. 获得模式 public String getScheme();
  2. 获得模式特定部分 public String getSchemeSpecificPart(); public String getRawSchemeSpecificPart();
  3. 获得片段 public String getFragment(); public String getRawFragment();
  4. 获得授权机构 public String getAuthority(); public String getRawAuthority() 授权机构包括:用户信息、服务器地址(域名或ip)、端口 user:password@localhost:80
  5. 获取片段标识 public String getFragment() public String getRawFragment()
  6. 获取服务器地址(域名或ip) public String getHost()
  7. 获取路径 public String getPath() public String getRawPath() 路径包括(目录结构和文件部分)。如:/dir/index.html
  8. 获取端口 public int getPort() 如果没有端口则返回-1;
  9. 获取URI的查询字符串 public String getQuery() public String getRawQuery()
  10. 获取用户信息 public String getUserInfo() public String getRawUserInfo()

如果URI是非透明只能获取到1~3个信息。 如果URI是层次结构则能获取所有信息。

方法中带Raw的,是获取编码后的URI部分信息。非ascii的字符需要进行编码,不带Raw的方法是解码后的信息。

getScheme、getHost、getPort这三个方法没有Raw方法,是因为这三部分不会出现非ascii的字符。

resolve 方法

resolve方法可以将相对URI转换成绝对URI。示例如下:

URI a = URI.create("http://localhost:8080/index.html");
URI b = URI.create("user/userInfo.html");
URI c = a.resolve(b);
System.out.println(c);

根据a获取b的绝对路径

打印结果为:http://localhost:8080/user/userInfo.html

源码如下
public URI resolve(URI uri) {
    return resolve(this, uri);
}

private static URI resolve(URI base, URI child) {
    // check if child if opaque first so that NPE is thrown
    // if child is null.
    if (child.isOpaque() || base.isOpaque())
        return child;

    // 5.2 (2): Reference to current document (lone fragment)
    if ((child.scheme == null) && (child.authority == null)
        && child.path.equals("") && (child.fragment != null)
        && (child.query == null)) {
        if ((base.fragment != null) && child.fragment.equals(base.fragment)) {
            return base;
        }
        URI ru = new URI();
        ru.scheme = base.scheme;
        ru.authority = base.authority;
        ru.userInfo = base.userInfo;
        ru.host = base.host;
        ru.port = base.port;
        ru.path = base.path;
        ru.fragment = child.fragment;
        ru.query = base.query;
        return ru;
    }

    // 5.2 (3): Child is absolute
    if (child.scheme != null)
        return child;
    URI ru = new URI();             // Resolved URI
    ru.scheme = base.scheme;
    ru.query = child.query;
    ru.fragment = child.fragment;

    // 5.2 (4): Authority
    if (child.authority == null) {
        ru.authority = base.authority;
        ru.host = base.host;
        ru.userInfo = base.userInfo;
        ru.port = base.port;

        String cp = (child.path == null) ? "" : child.path;
        if ((cp.length() > 0) && (cp.charAt(0) == '/')) {
            // 5.2 (5): Child path is absolute
            ru.path = child.path;
        } else {
            // 5.2 (6): Resolve relative path
            ru.path = resolvePath(base.path, cp, base.isAbsolute());
        }
    } else {
        ru.authority = child.authority;
        ru.host = child.host;
        ru.userInfo = child.userInfo;
        ru.host = child.host;
        ru.port = child.port;
        ru.path = child.path;
    }

    // 5.2 (7): Recombine (nothing to do here)
    return ru;
}
  1. 是否是非透明URI,如果是,则直接返回child。
  2. 判断child是否只有fragment(片段标识)。如果child只有片段标识则执行2.1。否则执行3。 2.1 如果child的fragment和base的片段标识一样,就直接返回base的url 2.2 把base不包含fragment的部分和child的fragment构造一个新的URI返回。
  3. 判断child的scheme不为空则直接返回child。不为空说明他是绝对路径。
  4. 根据base的URI各部分构造child的绝对路径URI并返回。

relativize 方法

relativize 方法可以将绝对路径的URI转换成相对路径的URI。

URI a = URI.create("http://localhost:8080/");
URI b = URI.create("http://localhost:8080/index.html");
URI c = a.relativize(b);
System.out.println(c);

获取b相对a的相对路径。

打印的结果为:index.html

private static URI relativize(URI base, URI child) {
    // check if child if opaque first so that NPE is thrown
    // if child is null.
    if (child.isOpaque() || base.isOpaque())
        return child;
    if (!equalIgnoringCase(base.scheme, child.scheme)
        || !equal(base.authority, child.authority))
        return child;

    String bp = normalize(base.path);
    String cp = normalize(child.path);
    if (!bp.equals(cp)) {
        if (!bp.endsWith("/"))
            bp = bp + "/";
        if (!cp.startsWith(bp))
            return child;
    }

    URI v = new URI();
    v.path = cp.substring(bp.length());
    v.query = child.query;
    v.fragment = child.fragment;
    return v;
}
  1. 判断child是否不是透明URI,如果不是则直接返回child。不是层次结构的uri是没有相对路径的。
  2. 判断两个URI的scheme和授权机构是否不同,如果不同则直接返回child。
  3. 判断base是否“/”结尾,如果不是则加上“/”
  4. 判断child是否以base开头,如果不是则直接返回child。
  5. 返回child中,不包含base的部分,构造一个新URI返回。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • RocketMQ 命令行工具源码结构解析

    RocketMQ 提供有控制台及一系列控制台命令,用于管理员对主题,集群,broker 等信息的管理;

    java404
  • Netty 之 AbstractNioByteChannel 源码分析

    1、该类定义了一个 flushTask 变量,来负责刷新发送已经 write 到缓存中的数据。write 的数据没有直接写到 socket 中,而是写入到 Ch...

    java404
  • RocketMQ CommitLog 文件规则

    偏移量:每个 CommitLog 文件的大小为 1G,一般情况下第一个 CommitLog 的起始偏移量为 0,第二个 CommitLog 的起始偏移量为 10...

    java404
  • 【SpringBoot系列】static修饰的字段如何获取application.yml配置

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    林老师带你学编程
  • 基础渲染系列(十)——更复杂的复合材质

    这是关于渲染的系列教程的第十部分。上一次,我们使用了多个纹理来创建复杂的材质。这次我们再增加一些复杂度,并且还支持多材质编辑。

    放牛的星星
  • 接口测试平台中接口内容的设计

    这部分主要是该条case的一些基础信息,例如创建人、最后更新人(同时也是最后调试过该case的人)。因为我们的平台有一个登录系统,所以所有case在被创建和被编...

    软件测试君
  • 【漏洞告知】CVE-2020-5902 F5 RCE

    F5提供业界领先的成套集成产品和服务。F5的产品能够消除带宽拥塞,并提高关键任务互联网服务器和应用系统的可用性和速度,其中包括Web发布、内容提...

    Aran
  • 聊聊nacos client的ServerListManager的start

    本文主要研究一下nacos client的ServerListManager的start

    codecraft
  • 聊聊nacos client的ServerListManager的start

    本文主要研究一下nacos client的ServerListManager的start

    codecraft
  • hdu----(2848)Repository(trie树变形)

    Repository Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K...

    Gxjun

扫码关注云+社区

领取腾讯云代金券