第一篇 为“JSP应用开发基础”
第1章 为什么要使用Servlet&JS P
JSP是J2EE平台体系中最重要的技术,是Java Web开发的核心。本章将阐述Servlet和JSP在网站开发中的作用,以及与其他动态页面技术的比较和区别。
通过本章的学习,读者将对Web有个总体认识,明白Web的运作过程和所用的技术,对Web容器、Servlet和JSP有个初步认识,懂得Web容器在开发Web程序中的地位和作用,知道Servlet、JSP的概念,以及它们和Web容器是如何协作实现Web服务的。
本章内容包括:
★ Web的概念和Web的运作机制。
★ Web应用的两个重要技术——HTTP和HTML。
★ 静态页面和动态页面。
★ Web容器的概念。
★ 初步认识Servlet和JSP。
进入第01章
1.1 Web网站
这里的Web是World Wide Web(缩写为WWW)的简称,也称为万维网。简单来说,Web是建立在Internet(国际互联网,也称因特网)之上的一种应用,它用URI(Uniform Resource Identifier,统一资源标识符)来标识分布在世界各地机器上的资源,如文本、图像等,客户可以根据URI来访问这些机器上的资源,以实现全球网络资源的共享。
注意:
人们经常混淆Internet(国际互联网)和World Wide Web(万维网)的概念。实际上,Internet指的是用TCP/IP协议联系起来的一个全球性互联网络,而Web是建立在Internet之上的应用之一,使用HTTP协议进行通信,如图1-1所示。其他的Internet应用还有电子邮件服务、FTP服务等。
图1-1 架构在Internet上的Web应用
从技术上来看,Web利用HTTP协议,采用客户端/服务器(Client/Server,简称C/S)的模式进行交互。提供Web服务的服务器一般称为Web网站,或者简称网站;而客户端最常见的就是浏览器,如Internet Explorer、Firefox等。
1.1.1 Web的运作机制
Web是以客户端/服务器的模式,通过HTTP协议来交互的。具体交互方式如下:
★ 一次通信过程包括请求和响应两个步骤。请求由客户端发起,请求信息中带有所需资源的URI和其他所需的信息。
★ 服务器收到请求后,根据URI和其他信息来决定如何处理,如可能找到资源文件并直接返回,也可能交给某个服务程序处理后再返回信息。
★ 如果成功,服务器返回响应信息和资源内容;如果出现问题,如资源不存在、客户无权限、服务器处理过程出现错误等,服务器则返回错误消息通知客户。
★ 不管获取资源是否成功,经过上述的请求/响应的步骤后,这次HTTP通信都会结束。后续的通信将发起新一轮的请求/响应,与本次无关。
运作过程如图1-2所示。
图1-2 Web的运作过程
资源的种类多种多样,因此服务器在返回资源内容时,会在响应信息中附带资源类型的说明,如说明这是图像、HTML页面还是一般的二进制文件等,客户程序会根据资源类型而采用相应的方式反馈给用户。如当客户程序是浏览器时,如果资源类型是HTML页面,它会将其解释出来并显示给用户;如果是一般的二进制文件,则可能弹出对话框让用户选择保存的地点。而当客户程序是下载工具时,则可能对任何资源类型都将它保存起来。
HTML页面,或者简称页面,是Web中应用得最广泛的一类资源。HTML页面也称为超文本(Hypertext),顾名思义,它是传统纯文本内容的扩充。本质上它只是一个普通的文本文件,但它用HTML(Hypertext Markup Language,超文本标记语言)写成,可以在页面中定义文字、图片等丰富多彩的内容。浏览器接收到页面后,会解释它并将定义的内容显示出来。页面中可以定义一种称为超链接(HyperLink)的内容,它包含其他资源的URI,用户单击它就能使浏览器转向该资源,以实现资源间的链接。
1.1.2 客户端和Web服务器
由前面内容知道,Web通信——或者说HTTP协议,是使用客户端/服务器模式的,这样就有客户端和服务器的概念。但它们不是指具体的机器,而是指实现客户端或服务器端功能的软件。
客户端软件种类很多,如最常用的浏览器、下载工具等,甚至是用户自己定制的程序。只要它能发送HTTP请求,并且能正确处理HTTP响应信息,就算是客户端。Web服务器也如此,能接受HTTP请求,并能返回HTTP响应的就可称之为服务器。本书中如无特别说明,客户端、服务器指的也是软件概念。
一般情况下,一台机器只充当一个角色。但既然客户端、服务器只是软件概念,一台机器能同时运行多个程序,当然也能同时充当这两个角色。Web代理正是这样的例子,如图1-3所示。
图1-3 Web代理既作为客户端也作为服务器
图1-3中,客户程序,如浏览器,需要从某服务器获取资源,但因为某些原因不能直接访问,如这个网站被屏蔽了,于是,它请求Web代理协助。
浏览器发送HTTP请求给Web代理,在请求信息中包含实际要访问的服务器地址。这时,浏览器充当客户角色,而Web代理则充当服务器的角色。
代理服务器可以和真正的服务器通信,于是,它根据请求信息,向真正的Web服务器发出请求,Web服务器接受请求并返回响应信息。这时,代理服务器扮演的是客户角色,而Web服务器则处于服务器的位置。
最后,代理服务器将收到的响应信息返回给浏览器,过程完成。在整个过程中,代理服务器既充当客户端,又充当服务器。
当然,Web代理和浏览器、Web代理和真正的Web服务器这两次通信是互相独立的HTTP通信过程。在浏览器的角度来看,“Web代理和真正的服务器通信”这一步可以抽象成图1-2中的“进行处理”这部分。
注意
为方便讲解,如无特殊说明,本章后面用“地址”代替URI,用“浏览器”代表客户程序。相信这更符合习惯,不会影响阅读。
1.2 HTML和HTTP
在上一节了解到,页面(或超文本)是一类很重要且应用最广泛的资源,它可以将文字、图像、超链接等丰富的内容呈现给用户。它之所以能实现这么强大的功能,是HTML和浏览器配合的结果。页面用HTML编写,可以定义各种格式的文字、图像、超链接等内容,而浏览器解释这些内容并将它显示给用户。
此外,Web是使用HTTP协议(Hypertext Transfer Protocol,超文本传输协议)进行通信的。HTTP这个名字可能让人认为它只能传输超文本类型的资源。实际上这个命名是因为该协议诞生时,Web主要用于页面传输;但实际上它也能传输其他如图像、文件等非页面数据。读者如有很丰富的网络使用经验,对这一点也会深有体会。
本节简单介绍HTML和HTTP,目的是给读者一个大概的认识,以方便后面的学习。读者如果有深入学习的需要,可以参考其他更详细的书籍和资料。
1.2.1 HTML标记语言
HTML是一种利用标签,用嵌套方式来描述内容的语言。“嵌套方式”是什么意思呢?如图1-4所示,它是由如图1-5所示例子使用的标签组成的树形结构。
图1-4 图1-5所示例子使用的标签组成的树形结构
图1-5 HTML文档例子
HTML文档是用标记来定义内容的,标记就是类似 <tag></tag> 形式的结构,前者是一个开始标记,后者则是结束标记。一般的标记都要求开始和结束配对使用,如<html></html>,<body></body>等。但因为历史原因,HTML的语法并不严格,有的标记,如例子中的<hr>,则没有结束标记,但可以将它看作是<hr></hr>这样一个完整标记的缩写。
标记之间只有两种关系,一种是互不重叠,如<tag1></tag1><tag2></tag2>;另一种是嵌套,如<tag1><tag2></tag2></tag1>,这时就说<tag1>嵌套<tag2>。除此之外,其他方式都是错误的,如<tag1><tag2></tag1></tag2>这种交叉嵌套的关系是非法的。
标签的这两种关系使得页面中定义的内容组成一个树形结构,如图1-4所示就是图1-5所示例子使用的标签所组成的树形结构。这种规定使得HTML页面能用简单的规则定义结构非常复杂的内容。
现在请看图1-5所示的例子,它给出了HTML文档和浏览器显示的内容。
HTML文档定义的所有内容都包含在 <html></html>中,其中包含<head></head>和<body></body>。<head></head>中主要定义一些页面的描述信息,如图1-5中的<title>定义了文档的标题,一般浏览器都将它显示在窗口的标题栏上。下一节提到的脚本语言和CSS样式表也可以在<head>中定义。<body></body>中定义页面要显示的内容。例子中,<a>所定义的是超链接、锚点,<hr>定义的是水平线,<table>定义的是表格,<form>定义的是表单等,都完全包含在<body></body>中。
从图1-5中可以看出,HTML允许在开始标签中添加属性,它们以“名”=“值” 的形式写出,一般用于对标签指定更详细的信息。如<a href=“..”>定义了一个超链接,而href这个属性值指定它链接到的地址。当用户单击时,浏览器就会跳转到这个地址。
开始和结束标签之间可以包含文字,它们能在页面上显示出来。比如图中的“<h1>例子</h1>”这行的“例子”两个字会显示出来,而<h1>标签则指示浏览器用一号字显示它。
开始和结束标签之间可以混合包含文字和嵌套标签,如图中定义表单的<form>标签中就是如此。
HTML页面中最有价值的标签之一是 “超链接”,它是通过 <a href=“URL”>显示的文字</a> 这样的形式来定义的,浏览器会显示标签中包含的“显示的文字”,如例子中定义了“到位置1”、“到位置2”这两个超链接。而当用户单击该超链接时,浏览器会跳转到href=“URL”所指定的地址。
页面不只是显示内容,还可以让用户填写数据并提交给服务器。<form>表单标记正是为此而设,表单中定义了各种收集数据的元素,浏览器以GUI显示给用户,如文本框、复选框等,用户在这些地方输入数据,然后单击“提交”按钮,即可将信息发送到服务器。本章后面将讨论更多有关<form>标签的内容。
注意
HTML从诞生到现在已经历了多个标准,最新的是HTML 4.01——实际上已经是最后一个版本了。由于HTML诞生初期的不严格,为了兼容性浏览器一般又采取了很多特殊的容错解释方式,有时同一个页面在不同的浏览器上的显示效果会不同。虽然有标准制定出来,但不同浏览器对某些标签的实现略有差别,甚至有的浏览器扩充了标签的类型。
HTML的后继者是XHTML,它的语法等要求严格很多,将来可望取代HTML,很可惜现在最新的浏览器对它的支持还不够,并且一般都默认禁止解释XHTML类型的页面。但在目前,如果要开发兼容多种浏览器的页面,调试工作是很重要但又很烦人的一环。
1.2.2 脚本语言JavaScript
传统的HTML页面,浏览器将其解释并显示后,它的内容就固定不再变化,用户不能跟页面做进一步的交互。但随着Web应用的不断增加,这些限制越来越不能满足要求。如当在表单输入数据时,希望能在客户端就进行部分的检查,而不是发送到服务器检查后,再返回出错信息,以提高交互性——这些用HTML是不可能实现的,但脚本语言就能派上用场。又如当页面有很多超链接时,为了界面友好,希望当用户的鼠标移动到其中一个上面时,将它用高亮度的颜色显示以提醒用户——这些用HTML也是不可能做到的,但CSS却能。
提示
人们常将HTML称为静态HTML,而将HTML、脚本语言、CSS结合起来的技术称为DHTML(Dynamic HTML,动态HTML)。
这里脚本语言是指的嵌在页面中的小程序,由浏览器解释执行,可以跟页面的内容做交互——或者更准确地说,页面定义的内容组成了一个DOM(Document Object Model,文档对象模型),而脚本语言可以读取和修改DOM,从而达到和页面内容交互的目的。
HTML页面最常用的脚本语言是JavaScript,它最开始是Netscape浏览器的扩展,经过几次改名后确定为JavaScript。IE也支持JavaScript,但却称之为JScript,同时IE也支持VBScript。这里只介绍JavaScript。
HTML页面中有两种方法可以定义脚本,先看如图1-6所示的例子。
图1-6 JavaScript脚本例子
本例中,粗体部分的代码就是脚本语言的定义,其中展示了两种定义方式。
★ 一种是包含在<script></script>标签中。本例中是通过function关键字定义了一个函数,可以被其他代码调用。浏览器允许<script>标记嵌入在任何的页面标签中,但一般情况下是将它放到<head></head>中。
★ 另一种方式是在HTML标签形如onXXX的属性值中定义。本例在<form>标签的onsubmit这个事件属性中添加了脚本,调用之前在<script>标签中定义的函数。
提示
除了用事件触发的方式使用脚本外,也可以直接将执行代码写在<script>标签中(而不是如图1-6所示例子中包含在函数定义中),这样,当浏览器解释页面时遇到它们就马上执行,不必等待某个事件被触发。但这时页面还未完全解释,DOM模型还未完全建立,很容易出现错误,使用时应尽量避免。
如图1-6所示的HTML页面定义了一个表单,它仅有一个“点击提交”按钮,用户单击“点击提交”按钮时浏览器会执行脚本。为了简单起见,这里仅弹出消息框通知用户脚本被触发。运行结果如图1-7所示。
图1-7 图1-6所示例子的运行结果
脚本程序调用的流程如下。
1 在事件属性中添加脚本
HTML的很多标签都定义了形如onXXX的属性,当这些事件被触发时,浏览器就会执行属性值中的代码。如本例中定义了“javascript:return checkSubmit();”,“javascript”这个标号告诉浏览器“:”后面的代码是JavaScript脚本,缺省时默认也是JavaScript。冒号后面的部分就是JavaScript代码了。
2 触发事件
onXXX对应着什么事件,这些事件是在什么情况下产生的,在HTML标准中有明确的定义。在本例中,<form>的属性“onsubmit”对应着“表单提交”这个事件,而这个事件在用户单击表单的“点击提交”按钮时产生。
3 浏览器执行事件属性值中的代码
当事件被触发后,浏览器就会执行事件属性值中的代码。在本例中,这部分代码调用之前定义的函数“submitCheck”,而该函数仅是弹出一个消息框。在实际开发中,可以在这里获取用户在表单输入的数据并检查是否有效。
4 代码通过返回值指示浏览器的后续动作
脚本程序结束时,可以返回状态值给浏览器,以指示浏览器后续的动作。如在本例中,“onsubmit”事件之后应该是浏览器将表单数据发送给服务器。如果程序返回true,浏览器确实会这样做;但本例中的程序返回false,这时浏览器会取消表单提交的动作。实际开发时,可以在“submitCheck”中检查用户的数据,当不符合要求时弹出消息框通知用户,并返回false让浏览器取消提交动作;如果符合要求,则返回true让浏览器提交。这样做将会大幅提高页面的交互能力。
注意
脚本语言也可以单独写在其他文件中,然后在页面中用<link>标记包含进来。如果同一段代码在多个页面中都用到,这种做法可以减少很多代码量。读者可查阅HTML的资料确定该标记的用法。
1.2.3 CSS样式表
在图1-5所示的HTML例子中可以看到,传统的HTML是将内容、显示风格混在一起的,例如<h1>例子</h1> 这一句,内容是“例子”两字,<h1>标签则是告诉浏览器用一号字显示。这种将内容和风格混杂在一起是不好的做法,原因如下:
★ 复杂性。将内容和显示方式混合使得页面的内容不清晰,结构复杂。现代的开发思想是尽量松耦合,而将内容风格混合的做法将使维护难度增大,难以扩展。
★ 灵活性。如果要改变页面显示风格时,需要对每个显示内容添加显示相关的标签和属性,即使同一类内容的显示风格一样,也要对每个内容都做修改。
CSS的出现在一定程度上解决了这些问题。将HTML和CSS配合使用,可以令页面专注于内容的定义,而通过CSS定义各种内容(标签)的显示风格。
现在看如图1-8所示的例子,它演示了如何在页面中使用CSS。这个例子定义了三个超链接,但却有不同的显示风格。
图1-8 CSS例子
例子中列举了两种定义CSS的方法。
一种是用<style></style>标签来包含,其内容用标签名{样式名:样式值; …} 的形式来书写。标签名指定了页面中的哪些标签可以用这个样式,样式名指定了要定义哪种显示风格,样式值则说明显示风格的类型。可以同时定义多个样式,它们之间用分号(;)分隔。
本例中,a{font-size:9pt; text-decoration:underline;}定义了页面中<a>标签所用的样式,这时<a>标签将默认使用这种风格。font-size定义了字体大小为9pt(pt表示磅,是一种字体大小的计量单位),text-decoration定义了字体用下画线(underline)来修饰。
例子中还使用了标签名.类型名{样式定义} 的方式,当标签通过class属性指定类型名时,将使用这里定义的风格。
本例中,a.special{font-size:12pt; text-decoration;}为<a>标签定义了另外的风格,其类型名为special。当用<a class=“special”>定义标签时,这种风格将应用在这个标签上。注意这只能用在<a>标签中,其他标签,如指定<h1 class=“special”>时是没有任何效果的。
还有一种定义方式是直接作为属性值写在标签中,如本例中最后一个链接就是如此。
注意
CSS样式也可以单独写在另一个文件中,然后用<link>标签包含到本页面。当这个样式在多个页面都使用时,可减少大量的重复工作。
1.2.4 什么是HTTP协议
HTTP是Hypertext Transfer Protocol的缩写,是Web应用的通信协议。它的命名并不表示它只能传输超文本页面,实际上它可以传输任何的数据类型,如图片、可执行文件等。
HTTP是一种基于文本并以请求/响应模式运行的协议,相应地,HTTP也有请求、响应两种消息类型,它们都是由消息头(header)和消息体(body)组成的。基于文本是指消息头的数据都是文字字符,可以直接用文本编辑工具查看其中的数据。HTTP一次运行过程的例子如图1-9所示。
图1-9 HTTP通信过程
如图1-9所示的例子演示了浏览器从服务器获取test.html这个页面的过程。需要注意的是,HTTP是一个应用级的协议,只关注与应用相关的信息,至于如何找到服务器的地址这些网络连接细节由底层的网络协议处理。如在Internet上,TCP/IP协议负责维护连接的细节问题,它通过域名服务系统(DNS),根据主机的地址查到服务器实际的IP地址,然后再处理连接。如本例中服务器地址是mywebsite.com。但在HTTP协议的角度无须理会这些细节,因此图1-9中简化成浏览器和服务器的直接通信。
当需要请求资源时,浏览器向服务器发送请求信息,完整的请求消息由消息头和消息体组成。
资源的内容(本例中为test.html这个页面的内容)在消息体中发送,因此在消息头中用Content-Length记录消息体内容的长度,而Content-Type则表示资源的类型。值得一提的是“Content-Type: text/html; charset=GBK”这一行,它告诉浏览器“资源类型是HTML页面,用GBK编码”。
消息头结束后,用一个空行和消息体分隔。消息体包含资源的内容,如当请求的是图片时,则它返回图片文件的二进制数据;本例中则是test.html这个页面的内容。
1.2.5 何谓URL
到目前为止,本章出现过URI、URL等词,Web应用通过URI、URL为资源标识,发送请求时用其指定资源。但它们究竟有什么区别呢,本节将说明这部分内容。
实际上,URI是Uniform Resource Identifier的缩写,而URL是Uniform Resource Locator(统一资源定位器)的缩写。它们的联系和区别如图1-10所示。
图1-10 URI和URL关系图示
严格来说,URI是指资源的标识符,但具体用什么方式标识,怎么解释标识符的含义,并没有做出明确规定;而URL是URI的一种具体实现,明确地用资源的地址来标识它。除URL外,URI也有其他具体实现方式,如URN等。
URI跟URL的关系好比标识一个人的身份所用的方式,URI就像是一个泛的概念,任何可以标识个人的方式都可以叫URI;而URL则类似利用住址来标识这个人——当然这里假设这些人都有很浓厚的定居意识,不会轻易迁徙。
注意
在Web中一般只使用URL方式定位资源,所以很多关于Web的文献中,URI、URL都是不加区分地使用,甚至有时候它们的含义略有变化。本书也将不加区分地使用,在有区别的时候会特别说明。
现在来讲讲URL的组成,最完整的URL的例子如图1-11所示。
图1-11 URL的组成
URL是用协议名开头的,用“://”和其他部分分隔。本例是HTTP协议,这也是Web使用的协议。
服务器在处理某些资源时,可能需要一些参数,根据参数而做出不同的响应。如在一个发布新闻的网站中,可能将所有新闻都用同一个资源地址定位,然后根据参数中的新闻ID返回具体的内容。资源地址和参数间用问号(?)分隔,参数之间用“&”分隔,参数是用name=value这样的形式给出的。
在URL的最后可以附上锚点名。使用锚点可以将页面定位到页面的某个指定位置。当页面返回后,浏览器会将该锚点位置的内容马上显示在可视窗口中。要注意的是,该锚点名并不影响服务器返回的内容,服务器还是将资源地址相关的资源内容全部返回,而不是只返回锚点名后面的内容。
在HTML页面中经常要用到URL,例如<a>标签等,URL可以用“绝对”和“相对”两种方法写出。绝对URL需要包括协议名、服务器地址和资源地址;而相对URL只需要包含资源地址的一部分即可。如图1-12所示的例子。
图1-12 页面中URL的写法
示例页面中有三个超链接,第一个里面的是绝对URL,单击该超链接时浏览器就直接利用该URL获取资源。
第二个是相对URL,单击时浏览器会首先将它跟该页面“参考URL的目录部分”合并成绝对URL。如在例子中,参考路径是http://mywebsite.com/mytest/file.html,其中目录部分是http://mywebsite.com/mytest/,超链接中的URL就是相对于这个地址。
默认情况下,页面的参考URL就是浏览器请求页面时所用的URL。如这里通过http://mywebsite.com/example/link.html获得该页面,则它自动成为参考路径。但通过<base>标记可以另外指定,如本例中将它改变为http://mywebsite.com/mytest/。注意<base>标记不需要</base>来结束。
页面的第三个超链接同样是相对URL,但它和第二个略有不同,它是以斜杠(/)开头的。这样表示它是相对于参考URL的服务器地址部分,或者说是相对于服务器的根目录,而不是相对于目录部分了。本例中<base>改变了参考URL为http://mywebsite.com/mytest/,而第三个URL为 /b.html,则合成的绝对URL为http://mywebsite.com/b.html。
1.2.6 HTML表单 和Web请求
很多情况下,需要在页面中让用户填写数据并提交到服务器。如在网站的注册页面中,需要填写用户名、密码等信息发送到服务器。为此,HTML提供了<form>标记,在里面可以定义收集数据的表单元素,浏览器将它以GUI形式显示,用户输入数据后提交表单,浏览器就会将表单数据附带在HTTP请求信息中发送到服务器去。
如图1-13所示的例子列举了页面中常用的表单元素。
图1-13 表单元素使用示例
<form>标签的action属性的值是一个URL,表示表单提交时请求信息发送到该地址。该URL可以是绝对或相对的,其含义可参考上一节的说明。
method属性是指发送这些数据所用的方法,这个方法就是HTTP请求时在第一行指定的方法。浏览器一般支持GET和POST两种,默认时是GET。下一节将讨论这两种方法的差异。
表单元素包含在<form></form>中,浏览器用GUI方式将它们显示出来。在本例中,使用了<input>、<select>、<textarea>三种标签,这些标签都有name属性。当往服务器发数据时,数据是用“名=值”这样的形式封装在HTTP请求中的,name属性的值就是这个“名”。
上面代码中仅列举了最常用的几个元素,表单中可以包含其他元素,如图像、文件上传框等。表单元素提供了很丰富的属性设置,还可以和脚本语言、CSS样式等结合。读者如有需要可参考更专门的资料,后面需要时也会提到。
1.2.7 GET和POST
在HTTP请求消息的第一行中有一个“请求方法”。而<form>的属性method的也告诉浏览器在请求时使用哪种请求方法。Web应用最常用的两种请求方法是GET和POST,本节将说明它们的实际含义和区别。
在HTTP请求信息中,GET方式表示不带有消息体,只含消息头——当然所有HTTP信息都带有消息头的;而请求方法为POST时则说明带有消息体。
对<form>来说,当指定了method=“GET”,提交表单时,浏览器会将要发送的数据附带在URL后,以请求参数的形式发送过去;而指定method=“POST”时,发送的数据在消息体中发送。
表单提 交时,使用GET方式发送参数如图1-14所示,使用POST方式发送参数如图1-15所示。
图1-15 表单指定POST方式时发送参数图示
提示
可能读者会问,能否在POST方式的请求中,既在第一行的URL中附加参数(像GET那样),同时又在消息体中附带其他参数呢?这是可以的<form action=“/test.jsp?id=1” method=“POST”>就可以做到。
实际使用时该如何选择方法呢?下面从以下几个方面看。
★ GET方式是附加在URL后面的,虽然HTTP协议对消息头的长度没有限制,但浏览器和服务器一般都有,因此用GET方式不能传送大量的数据,而POST方式则恰好相反,适合传送大量数据;
★ 用GET方式发送字符时,在非英语系的操作系统中,如果使用了非ASCII标准的字符编码,一般有两种发送方式。一是直接将字符直接附在URL后,如:
http://mywebsite.com/a.jsp?name=参数值
另一种是将它们用“%xx”的形式表示,%xx是该字符在页面编码中的字节值的16进制表示,如“参数值”这三个字的GBK编码(中文操作系统常用的编码)是“B2 CE CA FD D6 B5”,则发送时的形式可能如:
http://mywebsite.com/a.jsp?name=%B2%CE%CA%FD%D6%B5
这两种形式的差别给服务器进行编码转换带来不便。特别是在Java系统中,需要将所有字符都转换成Unicode编码,由于请求信息中是没有指定用哪种方式的,服务器很难决定如何处理,需要在设计时预先约定可行的规则。相反,POST方式则比较一致。
综上所述,GET方式应该用于参数很少,不带有非ASCII字符,且不会修改服务器状态的情况中,特别是对速度要求较高时要尽量考虑GET;而POST则相反,用于发送大量数据,或者带有非ASCII字符,或者需修改服务器状态等情况,或者在信息的准确性比速度更重要的时候。当然,这并非绝对,根据具体的需求做出合理的选择才是上策。
注意
HTTP中,除了GET,POST外,还定义了HEAD,PUT,DELETE等方法。但它们相对来说很少使用,而且浏览器一般只支持GET,POST,所以这里不再讨论。有需要可查阅资料,后面如果有需要也会提到。
1.3 动态页面
浏览器是通过URL来向服务器请求特定资源的,但资源的具体内容怎样得来,是由服务器决定的。资源可能只是某个文件的内容,服务器只是读取并返回;但也可能需要收集一些信息,组合后再生成具体内容。这就引出资源的两种生成方式——静态和动态;同样,页面作为资源的一种,自然也有静态页面和动态页面之分。
1.3.1 静态页面和动态页面
在Web应用产生之初,主要用于科学家之间进行信息交流,那时的信息量相对较少,而且最主要是信息的更新速度不快。当时Web主要用于页面传输,人们将信息预先写在HTML文件中并配置在服务器上,每当浏览器请求时,服务器就读取页面的内容并返回。这种方式生成的页面称为静态页面,其内容是预先确定的,对不同的请求都返回相同的内容。静态页面的获取流程如图1-16所示。
图1-16 获取静态页面示意图
然而,随着Web应用范围的扩大,静态页面越来越不能满足要求了。
★ 对于信息更新速度较快的应用,如果还使用静态页面,则每当信息变化时都要手动修改HTML文件。如果直接修改原来的文件,需要保证修改过程中服务器还能处理对该页面的请求;而修改后,又需要配置服务器使用更新后的版本,并且还要考虑修改过程中的人为错误等影响;
★ 对于信息更新不定时的应用,如24小时都有可能更新的,为了保证及时性,需要有专人24小时守护。但这样的开销是很大的,特别是在信息更新频率不高的场合,为了那不定时的少量修改而耗费大量时间是无法接受的;
★ 对于有一定规模的应用,数据往往是分散在多个地方的,单是关注信息的更新和收集就不容易,更不用说以上两点的影响了。
动态页面就在这样的需求下产生了。它和静态页面不同,服务器将请求交给专门的程序处理,程序在指定的地方收集数据,如通过数据库获取,或者通过网络获取远在万里之外的数据,然后组织在一起后再生成HTML格式的文本,最后服务器再组织成HTTP格式返回。动态页面的获取流程如图1-17所示。
图1-17 获取动态页面的示意图
依靠计算机的快速处理能力,动态页面使Web应用的范围不断扩大至今天的规模。
注意
不要混淆动态页面和DHTML(动态HTML)。动态页面是指页面的文档动态生成,是由服务器处理的;而DHTML是HTML、脚本语言和CSS的结合,使浏览器显示的页面内容可动态变化,使浏览器处理的。两者没有必然的联系。
1.3.2 Web容器的功能
通过前面的介绍,相信读者已经了解到Web应用的概貌了,也明白了Web应用的基础技术。而从开发的角度来看,Web服务器至少需要处理如下问题。
★ 因为Web是使用HTTP协议的,因此Web服务器必须能处理HTTP协议。这包括,能解释HTTP请求信息,提取出URL和附带的数据;能将资源内容封装成HTTP格式返回;
★ 浏览器只知道发送URL获取内容,而URL和具体资源的对应需要由服务器处理。如果是静态资源,则需要读取资源内容(如读取文件内容)并返回;如果是动态资源,则需要调用合适的处理程序,获取它生成的内容再返回;
★ 服务器必须能同时处理多个请求,因此要实现多个处理的同时进行,这需要服务器至少能管理线程(或进程)的建立、关闭等工作;
★ 处理请求的程序经常要在不同类型的地方获取数据,如文件、数据库、网络等。如果将和各类数据源的连接、获取数据、关闭等处理细节都下放到大量的程序中,将使程序变得很复杂。软件发展(其实很多方面也是如此)的一个方向就是抽象,将多种多样的处理细节隐藏起来,而提供给上层一个简单、一致的接口,这会使Web程序的编写变得方便,因此服务器需要提供这方面的功能。
可以看出,要考虑的内容真是又多又烦。如果一切都需要开发人员从头做起,Web绝对不会像今天这样应用得如此广泛。但分析一下上面列出的需求,会发现其中很多是通用的——如处理HTTP信息、管理线程的创建和关闭、对静态资源文件内容的读取等。而解决这些共同问题的方法就是使用Web容器了,其功能示意如图1-18所示。
图1-18 Web容器的功能
如图所示,Web容器解决了Web应用中的共同问题,剩下的只是与应用相关的问题,需要开发人员定制程序处理,这样使得Web开发、维护等工作更加高效快速。
当然了,定制的程序要能够和Web容器协调运作,它们之间必须遵守一定的协议。实际上,Web容器(或者说是它支持的技术,)就好比是Web应用开发的框架,开发人员必须按照这些技术的要求编写才能使程序正常运行。软件开发的困难之一就是从总体上做出正确的规划,定制出好的架构,从头做起的话是需要深厚的功底和丰富的经验的。而各种新技术的产生,不管它们是全新出现的还是建立在旧技术之上的,其中很重要的一个作用就是为开发人员定义出合适的框架,这样可以降低技术门槛,缩短开发流程,从而扩大应用范围。Web开发的各种新技术也不例外,而Web容器就是实现这些技术的软件产品。
1.3.3 Servlet的概念及其运作
Servlet直接解释的意思是“服务器端小程序”,它既是一种Web程序技术的名称,也是使用这些技术编写的程序的总称。先看一个具体的Servlet程序例子,该例子是返回一个非常简单的HTML页面内容,代码如图1-19所示。
图1-19 Servlet示例
注意
如果是初学者,可能会有很多看不明白的地方。这不要紧,这里最重要的是看看Servlet究竟“长什么样”,目的是有个具体认识,后面的章节会逐步解释它的结构和含义。
Servlet本质上是一个普通的Java类,但必须继承自javax.servlet.http.HttpServlet,否则Web容器不认识它。本例的Servlet名为HiServlet。
HttpServlet预定义了很多方法,其中覆盖doGet这个方法就可以处理GET类型的HTTP请求。还有其他如doPost、doXXX等方法,当收到不同类型的请求时由Web容器自动调用合适的方法。更详细的机制将在后续章节逐步学习。
根据这个例子,可以知道Servlet只不过是一个有特定要求的Java类而已,Web容器会调用合适的方法并传入参数,根据目的重载某些方法,就能实现和Web容器的合作了。当然,这只是从大体上来看,实际的机制、功能等内容是非常丰富的,后续章节会逐步学习。
在图1-17和图1-18中已经得知Web容器是如何与处理程序合作而生成动态资源的,Servlet作为一种具体技术,它的运作过程自然也类似,如图1-20所示。
图1-20 Servlet运作过程
图1-20中可看出,当接受请求时,要确定和该URL关联的资源。如果该URL相关联的是Servlet,则由Web容器调用Servlet进行处理。因此使用Servlet技术时要考虑两个问题,一是如何部署配置Web容器,使得URL跟Servlet联系起来;二是如何应用Servlet技术实现目的。
1.3.4 JSP的概念及其运作
在讲JSP前,先看看Servlet有什么不足。虽然现在还没正式学习Servlet,但这些讨论在前面的例子中也可以看出来,不会妨碍理解。
由前一节知道,Servlet只是一个有特定要求的Java类,因此它的编写跟普通的Java代码没什么两样。在图1-19的中还看到,当Servlet返回HTML文档时,是通过大量pw.println()之类的语句来实现的,可以看出,原来HTML的结构已经在这里分解得支离破碎了,生成动态页面的Servlet看起来就像是在Java代码中嵌入HTML代码。
实际开发中,HTML页面一般是比较大的,通常由专门的美工人员进行页面的设计,他们一般对Java程序不太熟悉;而编写业务逻辑的开发人员对页面设计又不如美工人员专业。这样,设计出来的HTML代码需要由开发人员逐个加到Servlet的println语句中。不难想像,这样的工作量是很大的,而且这样写出来的Servlet很难看,已经基本看不出原来HTML的结构了。而当页面需要修改时,又需要重复这样的过程。
可能我们会想,将HTML代码嵌入Java代码确实不好,但能否反过来,在HTML代码中嵌入Java代码呢?JSP正是按这一想法提出来的技术。先看一个具体的JSP例子,如图1-21所示。
图1-21 JSP例子
注意
跟Servlet的例子类似,现在读者可能会有很多东西看不明白。但最主要是看看JSP的样子,看它跟普通的HTML相比有哪些特殊东西即可。
从这里看出,JSP页面跟HTML页面很相似,只是其中多了一些特殊的标记<%%>,如<%@%>、<%=%>等。
JSP是Java Server Page(Java服务器端页面)的缩写。“页面”的得名是因为它表面看来是在HTML中嵌入Java代码,和普通的HTML页面很类似;而“服务器端”是指页面会在服务器端进行处理,而普通的HTML页面只在浏览器中才被解释,服务器只是返回内容,并不关注具体的内容是什么。
明白JSP页面中哪些内容会被Web容器处理、怎么处理,是掌握JSP的一个关键。实际上,只有包含在<%%>标签中的内容才会被Web容器处理;在JSP 2.0规范中新提出了表达式语言(Expression Language,简称EL),在页面中是以“${表达式}”的方式来描述的,支持JSP 2.0规范的Web容器也会处理这些内容。除此以外,Web容器忽略其他内容,只将它看成是普通的字符数据。本书第三部分是专门讨论JSP的,到时会再详细讨论相关机制和细节,现在只需要知道JSP的大概即可。
注意
初学者很容易分不清服务器脚本和页面脚本的区别,而将写在JSP页面中的JavaScript代码误认为是在服务器端执行的,如上例中的“<body onload=“alert(‘hello!’)”>”。但正如上一段所讲,Web容器只关注<%%>标记内的内容,而对支持JSP 2.0规范的Web容器则也关心 “${} ”中的内容,其他则一概不管,直接返回给浏览器。
表面看来,JSP似乎是一种全新的动态资源技术,用于解决Servlet的不足并取代它。但实际上不是这样的,JSP是以Servlet为基础的。Web容器在处理JSP时,会首先将它编译成Servlet,然后再调用Servlet进行实际的处理工作的。JSP的运作过程如图1-22所示。
图1-22 JSP的运作过程
从图1-22中可以看到,实际进行处理的还是Servlet,但JSP和Servlet的维护关系由Web容器负责。
当请求JSP时,Web容器会检查当前JSP和Servlet是否一致。这通常是用文件修改日期的先后来判断的,如果这个JSP还没转成Servlet,或者在前一次转换之后又经过修改,导致JSP文件的修改日期比Servlet的要迟,Web容器会重新执行Servlet的生成过程。它将JSP转换成等价的Servlet源文件,然后调用Java编译器编译成Class文件,再重新加载并调用这个Servlet进行请求。但如果两者是一致的,Web容器就直接调用之前生成的Servlet进行处理。
从图1-22中还可以看到,生成Servlet后,请求都是直接由Servlet处理的,中间花费在文件日期比较的时间只是以微秒级计算(硬盘操作),像数据库、网络这些以毫秒级计算的此类操作几乎可以忽略不计。但尽管如此,不少Web容器还提供了关闭JSP到Servlet自动更新的功能,这样在Web程序运行期间,修改后的JSP就不能马上生效了。这一般需要配合预编译功能,并用在几乎不会修改的情况下。
提示
从JSP生成Servlet一般需要较长的时间,如几秒甚至几十秒。如果JSP页面是新部署到Web服务器的,处理第一次请求时将要进行转换工作,这样第一次请求需要等待的时间较长。为此,不少Web容器都提供了预编译的功能,可以在部署时就将JSP转成Servlet,无须等到第一次请求。
现在知道,Servlet和JSP都是动态资源生成的技术,并且JSP是以Servlet为基础的。从功能上来看,JSP跟Servlet的作用是等价的。
技术的出现的主要目的是为了方便使用,Servlet可以生成任何类型的动态资源,非常灵活,但对于动态页面的生成,虽然理论上可以做到,但实际应用时却显得很笨拙。为了解决这个问题,产生了JSP技术。虽然JSP最终还是要转成Servlet的,但从使用角度来看,它就像是在HTML代码中嵌入Java代码,使得动态页面程序的编写变得相当直观,比起实现同等功能的Servlet,无论开发效率还是维护性都得到提高。实际上,JSP技术很适合用于生成文本类型的动态资源。
但Servlet也并非从此就退居幕后,Web应用中非文本类的动态资源也很常用,如现在不少网站都提供了验证码的功能,显示验证码的图片就是动态生成的,这时用Servlet编写将更合适;用JSP的话,页面中只有代码而没有标签,显得很奇特。而且,实际的Web程序中除了生成动态资源外,往往还需要很多其它的控制功能,如跳转到其他处理程序、过滤非法用户的请求等,这时使用Servlet将是更合理的选择。如图1-23所示为两种技术的关系及其适用范围。
图1-23 Servlet和JSP的关系及适用范围
Servlet和JSP并非互相取代的,而是各有所长,实际中也经常将两者配合使用。任何技术都有其长处和不足,Servlet和JSP也如此。学习技术不但要理解它的内容,更要懂得如何将技术应用在能发挥最大效用的地方。
1.4 小结
本章学习了Web的基础知识和初步了解Servlet、JSP,现在来总结一下。
Web是以Internet为基础的应用之一,它是以HTTP为通信协议,以客户端/服务器的请求/响应模式来运行的。
在Web中,服务器的资源通过URL来定位。客户端通过URL定位服务器,然后向服务器发出HTTP请求;服务器通过URL定位资源,然后将资源以HTTP响应的方式返回。
Web应用中有一类很重要的资源,称为超文本页面,或者简称页面,它用HTML写成,由浏览器解释并显示给用户。页面中包含有超链接、表单、文字、图像等内容,可以提供丰富的信息,可以在资源间跳转,也可以让用户跟服务器进行交互。正是其丰富的功能使得Web的应用如此广泛。
Servlet和JSP是以Java技术为基础的Web应用开发技术。Servlet常用于非文本动态资源的生成,以及实现控制逻辑;而JSP则常用文本类动态资源(如动态页面)的生成,以简化开发难度。JSP是以Servlet为基础的,但两者却并非可以互相替代,而是相辅相成的。