Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3472536
  • 博文数量: 1450
  • 博客积分: 11163
  • 博客等级: 上将
  • 技术积分: 11101
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-25 14:40
文章分类

全部博文(1450)

文章存档

2017年(5)

2014年(2)

2013年(3)

2012年(35)

2011年(39)

2010年(88)

2009年(395)

2008年(382)

2007年(241)

2006年(246)

2005年(14)

分类: Java

2007-02-13 10:46:11

作者: 陶刚编译
时间: 2003-08-26
出处: yesky
责任编辑: 方舟

[文章导读]

本文分析了URI、URL和URN的组成和体系结构,并探讨了如何在程序中使用Java API的URI和URL类


==================================================================================

URI、URL和URN是识别、定位和命名互联网上的资源的标准途径。本文分析了URI、URL和URN和Java API的URI和URL类(以及与URL相关的类),并演示了如何在程序中使用这些类。

  1989年Tim Berners-Lee发明了互联网(World Wide Web)。WWW被认为是全球互连的实际的和抽象的资源的集合--它按需求提供信息实体--通过互联网访问。实际的资源的范围从文件到人,抽象的资源包括数据库查询。因为要通过多样的方式识别资源(人的名字可能相同,然而计算机文件只能通过唯一的路径名称组合访问),所以需要标准的识别WWW资源的途径。为了满足这种需要,Tim Berners-Lee引入了标准的识别、定位和命名的途径:URI、URL和URN。

  URI、URL和URN是什么?

  体系中的URI、URL和URN是彼此关联的。URI的范畴位于体系的顶层,URL和URN的范畴位于体系的底层。这种排列显示URL和URN都是URI的子范畴,如图1所示:


图1:URI、URL和URN之间的层次关系。URL和URN是URI的子范畴。

  URI表示的是统一的资源标识,它是以某种统一的(标准化的)方式标识资源的简单字符串。典型情况下,这种字符串以scheme(命名URI的名字空间的标识符--一组相关的名称)开头,语法如下:

  [scheme:] scheme-specific-part

  URI以scheme和冒号开头。Scheme用大写/小写字母开头,后面为空或者跟着更多的大写/小写字母、数字、加号、减号和点号。冒号把scheme与scheme-specific-part分开了,并且scheme-specific-part的语法和语义(意思)由URI的名字空间决定。其中一个例子是,其中http是scheme,//是 scheme-specific-part,并且它的scheme与scheme-specific-part被冒号分开了。
我们可以把URI按照绝对的或相对的分类。绝对的URI指以scheme(后面跟着冒号)开头的URI。前面提到的就是绝对的URI的一个例子,其它的例子还有mailto:jeff@javajeff.com、news:comp.lang.java.help和xyz://whatever。你可以把绝对的URI看作是以某种方式引用某种资源,而这种方式对标识符出现的环境没有依赖。如果使用文件系统作类比,绝对的URI类似于从根目录开始的某个文件的路径。与绝对的URI不同的,相对的URI不是以scheme(后面跟着冒号)开始的URI。它的一个例子是articles/articles.html。你可以把相对的URI看作是以某种方式引用某种资源,而这种方式依赖于标识符出现的环境。如果用文件系统作类比,相对的URI类似于从当前目录开始的文件路径。

URI可以进一步分为不透明的和分层的两类。不透明的URI指scheme-specific-part不是以正斜杠(/)开头的绝对的URI。其例子有news:comp.lang.java和前面的mailto:jeff@javajeff.com。不透明的URI并不是用于分解的(超出了识别scheme的范畴),因为不需要验证scheme-specific-part的有效性。与它不同的是,分层的URI可以是以正斜杠开头的绝对的URI或相对的URL。

  与不透明的URI不同,分层的URI的scheme-specific-part必须被分解为几个组成部分。这些组成部分是什么?分层的URI标识组件的普通子集的scheme-specific-part符合下面的语法:

  [//authority] [path] [?query] [#fragment]

  可选的authority组件标识了该URI名字空间的命名机构。如果有这一部分,它就是以一对正斜杠开始的,它可以是基于服务器或基于注册的,并且它以后面的正斜杠、问号或没有其它符号结束。基于注册的授权机构组件有特定大纲的语法(本文没有讨论,因为很少使用它),而基于服务器的授权机构组件的语法如下:

  [userinfo@] host [:port]

  按照这种语法,基于服务器的授权机构组件可以随意的以用户信息(例如用户名)开始,后面跟着一个@符号,紧接着是主机的名称,以及冒号和端口号。例如jeff@x.com:90就是一个基于服务器的授权机构组件,其中jeff包含了用户信息,x.com包含了主机,90包含了端口。

  可选的path组件根据授权机构组件(如果提供了)或大纲(如果没有授权机构组件)识别资源的定位(或位置)。路径(path)可以分成一系列的路径片断(path segment),每个路径片断使用正斜杠与其它的路径片断隔开。如果路径的第一个路径片断以一个正斜杠开始,该路径就被认为是绝对的。否则路径就被认为是相对的。例如,/a/b/c由三个路径片断--a、b和c组成了一个路径,此外,这个路径是绝对的,因为第一个路径片断(a)的前缀是正斜杠。

  可选的query组件识别要传递给某种资源的数据。这种资源使用该数据获取或生成其它的传递回调用者的数据。例如, x=y就是一个查询(query),在这个查询中,x=y是传递给某种资源的数据--x是某种实体的名称,y是该实体的值。

  最后一个组件是fragment。尽管该组件作为URI的一部分出现,但不是绝对的。当使用URI进行某种检索操作时,后面执行操作的软件使用fragment聚焦于软件感兴趣的资源部分(在该软件成功检索到资源的数据后)。

  为了实际表现前面提到的组件信息,可以使用下面的URI:

  ftp://george@x.com:90/public/notes?text=shakespeare#hamlet

  上面的URI把ftp识别为大纲,把george@x.com:90识别为基于服务器的授权机构(其中george是用户信息,x.com是主机,90是端口),把/public/notes识别为路径,把text=shakespeare识别为查询,把hamlet识别为片断。本质上它是一个叫做george的用户希望通过/public/notes路径在服务器x.com的90端口上检索shakespeare文本的hamlet信息。在shakespeare成功的返回到该程序后,程序定位hamlet段并把它呈献给该用户。

标准化可以通过目录术语来理解。假定目录x直接位于根目录之下,x有子目录a和b,b有文件memo.txt,a是当前目录。为了显示memo.txt中的内容(在微软Windows下),你可能输入type \x\.\b\memo.txt。你也可能输入type \x\a\..\b\memo.txt,在这种情况下,a和..的出现是没有必要的。这两种形式都不是最简单的。但是如果输入\x\b\memo.txt,你就指定了最简单的路径了,从根目录开始访问memo.txt。最简单的\x\b\memo.txt路径就是标准化的路径。

  通常通过基本的和相对的URI访问资源。基本的URI是绝对的URI,它唯一地标识了某种资源的名字空间,而相对的URI标识了与基础的URI相对的资源。(与基本的URI不同,相对的URI在某种资源的生存周期内可以永远不需要改变)。因为基本的和相对的URI都不能完整的识别某种资源,有必要把两种URI通过解析过程合并。相反地,通过相对化从合并的URI中提取相对的URI也是可行的。

  注意

  不透明的URI与其它的URI不同,它不服从标准化、分解和相对化。

  假定你把x://a/作为基础的URI,并把b/c作为相对的URI。根据基础URI分解这个相对的URI将产生x://a/b/c。根据x://a/相对化x://a/b/c将产生b/c。

  URI不能定位或读取/写入资源。这是统一的资源定位器(URL)的任务。URL是一种URI,但是它的大纲组件是已知的网络协议(简称协议),并且它把URI组件与某种协议处理程序(一种资源定位器和根据协议建立的约束规则与资源通讯的读/写机制)。

  URI一般不能为资源提供持久不便的名称。这是统一的资源命名(URN)的任务。URN也是一种URI,但是全球唯一的、持久不便的,即使资源不在存在或不再使用。

使用URI

  网络API通过提供了URI类(位于java.net程序包中),使我们在源代码层使用URI成为可能。URI的构造函数建立了封装URI的URI对象;URI的方法建立URI对象;如果授权机构组件是基于服务器的就分析它,提取URI组件,决定URI对象的URI是绝对的还是相对的;决定URI对象的URI是不透明的还是分层的;比较两个URI对象中的URI;标准化(normalize)URI对象的URI;根据URI对象的基础URI分解某个相对的URI以得到已分解的URI;根据URI对象的基础URI关联某个已分解的URI以得到相对的URI,把URI对象转换为URL对象。
我们进一步查看URI类,在它里面有五个构造函数。最简单的是URI(String uri)。这个构造函数把URI作为String类型的参数,把URI分解为组件,并把这些组件存储在一个新的URI对象中。如果String对象的URI(通过uri引用)违反了RFC 2396的语法规则,其它的四个构造函数URI(String uri)将会产生一个java.net.URISyntaxException对象。

  下面的代码片断演示了使用URI(String uri)建立封装了一个简单的URI组件的URI对象:

URI uri = new URI ("");

  典型情况下URI构造函数用于建立封装用户指定的URI的URI对象。因为用户可能输入不正确的URI,所以URI构造函数产生已检查的URISyntaxException对象。这意味着你的代码必须明确地尝试着调用某个URI构造函数并捕捉异常,或者通过在该方法的Throws子句中列举URISyntaxException以"推卸责任"。

  如果你知道URI是有效的(例如在源代码中的URI),将不会产生URISyntaxException对象。因为在这种情况下处理某个URI构造函数的异常处理要求可能有困难,所以URI提供了静态的create(String uri)方法。这个方法分解通过uri引用的String对象中包含URI,如果该URI没有违反任何语法规则就建立URI对象(并从方法中返回对它的引用),否则将捕捉到一个内部的URISyntaxException对象,把该对象包装金一个未检查的IllegalArgumentException对象中,并抛出这个IllegalArgumentException对象。因为IllegalArgumentException是未检查的,你不需要明确的尝试代码并捕捉异常或把它的类名称列举在Throws子句中。

  下面的代码片断演示了create(String uri):

URI uri = URI.create ("");

  URI构造函数和create(String uri)方法试图分解出某个URI的授权机构组件的用户信息、主机和端口部分。对于按正常形式形成的基于服务器的授权机构组件,它们是会成功的。对于按拙劣的形式形成的基于服务器的授权机构组件,他们将会失败--并且把该授权机构组件当作是基于注册的。有时你可能知道某个URI的授权机构组件必须是基于服务器的。你可以确保该URI的授权机构组件分解出用户信息、主机和端口,或者你可以确保将产生一个异常(伴随着相应的诊断信息)。你可以通过调用URI的parseServerAuthority()方法实现这种操作。如果成功分解出URI,该方法将返回包含提取的用户信息、主机和端口部分的URI的新URI对象的一个引用(但是如果授权机构组件已经被分解过了,将会返回调用parseServerAuthority()的URI对象的引用。),否则该方法将产生一个URISyntaxException对象。

  下面的代码片断演示了parseServerAuthority():

// 下面的parseServerAuthority()调用出现后会发生什么情况?
URI uri = new URI ("//foo:bar").parseServerAuthority();


一旦拥有了URI对象,你就可以通过调用getAuthority()、getFragment()、getHost()、getPath()、getPort()、getQuery()、getScheme()、getSchemeSpecificPart()和 getUserInfo()方法提取多种组件。你也可以通过调用isAbsolute()确定该URI是绝对的还是相对的,通过调用isOpaque()确定该URI是不透明的还是分层的。如果返回值是true意味着该URI是绝对的或不透明的,如果返回值是false意味着该URI是相对的或分层的。

  列表1中的程序用命令行参数建立了一个URI对象,调用URI组件提取方法来检索URI的组件,并调用URI的isAbsolute()和isOpaque()方法把该URI分类为绝对的/相对性和不透明的/分层的。

  列表1: URIDemo1.java

// URIDemo1.java

import java.net.*;

class URIDemo1
{
public static void main (String [] args) throws Exception
{
if (args.length != 1)
{
System.err.println ("usage: java URIDemo1 uri");
return;
}

URI uri = new URI (args [0]);

System.out.println ("Authority = " +uri.getAuthority ());
System.out.println ("Fragment = " +uri.getFragment ());
System.out.println ("Host = " +uri.getHost ());
System.out.println ("Path = " +uri.getPath ());
System.out.println ("Port = " +uri.getPort ());
System.out.println ("Query = " +uri.getQuery ());
System.out.println ("Scheme = " +uri.getScheme ());
System.out.println ("Scheme-specific part = " +
uri.getSchemeSpecificPart ());
System.out.println ("User Info = " +uri.getUserInfo ());
System.out.println ("URI is absolute: " +uri.isAbsolute ());
System.out.println ("URI is opaque: " +uri.isOpaque ());
}
}

  输入java URIDemo1命令后,列表1的输出结果如下:

query://jeff@books.com:9000/public/manuals/appliances?stove#ge:
Authority = jeff@books.com:9000
Fragment = ge
Host = books.com
Path = /public/manuals/appliances
Port = 9000
Query = stove
Scheme = query
//jeff@books.com:9000/public/manuals/appliances?stove
User Info = jeff
URI is absolute: true
URI is opaque: false

  上面的输出显示该URI是绝对的,因为它指定了一个大纲(query),并且该URI是分层的,因为query后面有/符号。

  技巧

  你应该调用URI的compareTo(Object o)和equals(Object o)来决定URI的次序(为了排序目的)和等同性。你可以参考SDK文档,查阅这些方法的更多信息。

一旦拥有了URI对象,你就可以通过调用getAuthority()、getFragment()、getHost()、getPath()、getPort()、getQuery()、getScheme()、getSchemeSpecificPart()和 getUserInfo()方法提取多种组件。你也可以通过调用isAbsolute()确定该URI是绝对的还是相对的,通过调用isOpaque()确定该URI是不透明的还是分层的。如果返回值是true意味着该URI是绝对的或不透明的,如果返回值是false意味着该URI是相对的或分层的。

  列表1中的程序用命令行参数建立了一个URI对象,调用URI组件提取方法来检索URI的组件,并调用URI的isAbsolute()和isOpaque()方法把该URI分类为绝对的/相对性和不透明的/分层的。

  列表1: URIDemo1.java

// URIDemo1.java

import java.net.*;

class URIDemo1
{
public static void main (String [] args) throws Exception
{
if (args.length != 1)
{
System.err.println ("usage: java URIDemo1 uri");
return;
}

URI uri = new URI (args [0]);

System.out.println ("Authority = " +uri.getAuthority ());
System.out.println ("Fragment = " +uri.getFragment ());
System.out.println ("Host = " +uri.getHost ());
System.out.println ("Path = " +uri.getPath ());
System.out.println ("Port = " +uri.getPort ());
System.out.println ("Query = " +uri.getQuery ());
System.out.println ("Scheme = " +uri.getScheme ());
System.out.println ("Scheme-specific part = " +
uri.getSchemeSpecificPart ());
System.out.println ("User Info = " +uri.getUserInfo ());
System.out.println ("URI is absolute: " +uri.isAbsolute ());
System.out.println ("URI is opaque: " +uri.isOpaque ());
}
}

  输入java URIDemo1命令后,列表1的输出结果如下:

query://jeff@books.com:9000/public/manuals/appliances?stove#ge:
Authority = jeff@books.com:9000
Fragment = ge
Host = books.com
Path = /public/manuals/appliances
Port = 9000
Query = stove
Scheme = query
//jeff@books.com:9000/public/manuals/appliances?stove
User Info = jeff
URI is absolute: true
URI is opaque: false

  上面的输出显示该URI是绝对的,因为它指定了一个大纲(query),并且该URI是分层的,因为query后面有/符号。

  技巧

  你应该调用URI的compareTo(Object o)和equals(Object o)来决定URI的次序(为了排序目的)和等同性。你可以参考SDK文档,查阅这些方法的更多信息。

阅读(900) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~