第3章 servlet 基础
1. servlet 的生命周期。
a) init方法:首次创建servlet时,它的init方法会得到调用,因此,init是放置一次性设置代码的地方。
i. 可以使用此方法进行常规初始化,用来创建或载入在Servlet生命期内用到的一些数据,或者执行某些一次性的计算。
ii. 由初始化参数控制的初始化。
要理解init参数的动机,您需要了解什么样的人可能希望对Servlet的行为方式进行定制。其中包括:开发人员、最终用户、部署人员。
开发人员通过改变代码改变Servlet的行为。
最终用户通过向HTML表单提供数据改变Servlet的行为。
部署人员为了能够在不修改Servlet的源代码的情况下,就可以将Servlet在机器间移动,以及改变特定的参数(例如,数据库的地址,连接共享大小,或者数据文件的位置)。init参数的目的就是为了提供这项能力。
b) service方法:在init之后,针对每个用户请求,都会创建一个纯种,该线程调用前面创建的实例的service方法。service方法检查HTTP请求的类型(GET,POST,PUT,DELETE等)并相应地调用doGet,doPost,doPut,doDelete等方法。
c) doGet,doPost和doXxx方法
GET请求起因于正常的URL请求,或没有指定METHOD的HTML表单。POST请求起因于特别将POST列为METHOD的HTML表单。你可以覆盖doGet和/或doPost方法来处理。
DELETE 请求由doDelete处理,PUT 由doPut方法处理,OPTIONS 由doOptions处理,TRACE 由doTrace方法处理。OPTIONS和TRACE的请求在Servlet中是自动支持的。
doHead以处理HEAD请求(HEAD请求规定,服务器应该只返回正常的HTTP头,不含与之相关联的文档)。为了能够更快地生成对HEAD请求的响应(例如来自定制客户的请求,只需要HTTP报头,不需构建实际的文档输出),会实现doHead方法。
2. SingleThreadModel接口
a) 通过让Servlet实现这个接口,系统会保证不会有多个请求线程同时访问该servlet的单个实例。大多数和情况下,系统将所有的请求排队,一次只将一个请求转给单个servlet实例。
b) 此接口已经过时,如果要同步最好使用synchronized关键字。
第4章 客户请求的处理:表单数据
1. 表单数据的读取。
a) 单个值的读取:getParameter(表单项名称),表单项名称大小写敏感。如果表单项存在但没有相应的值,返回空的String;如果没有这样的表单项,则返回null。
b) 多个值的读取:getParameterValues(表单项名称),如果同一表单项名称在表单数据中多次出现,则应该调用getParameterValues(返回字符串的数组)。对于不存在的表单项名称,getParameterValues的返回值是null,如果参数只有单一的值,则返回只有一个元素的数组。
如果是HTML表单的设计者,最好保证每个文本字段、复选框或其他用户界面元素都有一个唯一的名称。
2. 参数名的查找:getParameterNames和getParameterMap
getParameterNames以Enumeration的形式返回表单项名称列表,其中的的每一项都可以转换成String,并可以用在getParameter或getParameterValues调用中。如果当前请求中没有表单名称返回空的Enumeration(不是null)。
Enumeration只是一个接口,它保证实际的类实现了hasMoreElements和nextElement方法:它并不保证具体的实现会采用某种特定的底层数据结构。
getParameterNames的替代方案是getParameterMap。这个方法返回一个Map:表单名称(字符串)是表的键,表单项的值是表的值。
3. 读取上载的文件和原始数据:getReader或getInputStream
当数据不是HTML表单提交,而是来自于定制的客户程序时,可能需要自己读取和分析这些数据。最常见的客户程序是applet。
当数据来自于上载的文件时,可能需要自己读取数据。servlet的API没有定义任何机制来读取<input type=”file”>元素的文件。
4. 多字符集输入的读取:setCharacterEncoding(字符集名称)
request.getParameter使用服务器的当前字符集解释输入。要改变这种默认行为,需要使用ServletRequest的setCharacterEncoding方法。setCharacterEncoding必须在访问任何请求参数之前调用。
我们可以按照某个字符集读取参数,然后将它转换到另外的字符集;或者用某些字符集提供的自动检测特性。
第一种方案:使用getByte提取出原始的字节数据,之后将这些字节连同期望字符集的名称一同传递给String的构造函数。
例:
String firstNameWrongEncoding = request.getParameter(“firstName”);
String firstName = new String(firstNameWrongEncoding.getBytes(), “shift_JIS”);
第二种方案:需要使用一种支持从默认字符集进行检测和转换的字符集。
例:如果允许输入既可以是英语,也可以是日语,则要使用下面的语句:
request.setCharacterEncoding(“JISAutoDetect”);
String firstName = request.getParameter(“firstName”);
5. 参数缺失或异常时默认值的应用
如果用户没有提供必需的信息,那么该servlet应该怎么处理这种情况
a) 使用默认值
b) 重新显示这个表单(提示用户缺失的值)。
c) 请求中的参数需要检查下面三种情况:
i. 参数的值为null
用户使用了错误的表单或使用了包含GET数据的URL书签,但在制作URL书签之后,参数名发生变化,都会发生返回值为null的情况。
ii. 参数的值为空字符串(“”)
用户没有输入指定的值。为了安全起见,最好调用trim,移除用户可能输入的任何空格。
iii. 参数的值为非空字符串,但格式错误
在长度和类型(只要求数值的字段)上输入错误。在设计servlet时,要使之能够优雅地处理参数缺失(null或空字符串)或格式不正确等情况。
6. 过滤字符串中的HTML特殊字符
a) 如果servlet希望生成含有诸如<或>等字符的HTML,只需简单地使用标准的HTML字符实体——<或>。
b) 如果您需要读取请求参数,并将它们的值显示在生成的页面中,则必须过滤出那些特殊的HTML字符。不这样做可能会导致输出中缺失部分,或者某些部分格式错误。
c) Java字符串虽不可改变的(即不能修改),因此,重复的字符串拼接操作需要复制许多字符串片段,并在使用后废弃。建议在循环中执行重复性的拼接操作时,应该使用可以改变的数据结构:StringBuffer是通常的选择。
7. 根据请求参数自动填充Java对象:表单bean
a) 普通的Java对象,如果它所属的类使用私有字段,且拥有遵循get/set命令约定的方法,则可以看作是bean。方法名(除去单词“get”或”set”,并且首字母小写)称为属性(property)。
8. 当参数缺失或异常时重新显示输入表单。
a) 由同一servlet提供表单、处理数据并提供最后的结果。
表单省略Action属性,从而,表单提交时会自动发送到表单自身的URL。
b) 由一个servlet提供表单:由第二个servlet处理数据并提供结果。
从一个servlet转到另外的servlet可以使用response.sendredirect或RequestDispatcher的forward方法。将数据从负责处理的servlet传递回显示表单的servlet时,最简单的方式是将它存储在HttpSession对象中。
c) 由一个JSP页面“手动地”提供表单:由一个servlet或JSP页面处理数据并提供结果。
d) 由一个JSP页面提供表单,用从数据对象获取的值自动填写表单中相应的字段:由一个servlet或JSP页面处理这些数据并提供最终结果。
第5章 客户请求的处理:HTTP请求报头
1. 简介:请求报头在JSP中的读取及应用与在servlet中相同。请求报头由浏览器间接地设定,并紧跟在初始的GET和POST请求行之后发送。
2. 请求报头的读取。
只需用报头的名称为参数,调用HttpServletRequest的getHeader方法。如果当前的请求中提供了指定的报头,则这个调用返回一个String,否则返回null。在HTTP1.0中,请求的所有报头都是可选的:在HTTP1.1中,只有Host是必需的。因而,在使用请求报头之前一定要检查是否为null。报头名称对大小写不敏感。
a) 访问HTTP报头的方法汇总。
getHeader是读取输入报头的通用方式。访问其他常用报头的方法有:
i. getCookies方法返回Cookie报头的内容。
ii. getAuthType和getRemoteUser
返回Authorization报头进行拆分,分解成它的各个构成部分。
iii. getContentLength
返回Content-Length 报送的值(作为一个int值返回)
iv. getContentType
返回Content-Type报头的值(作为一个String返回)
v. getDateHeader 和 getIntHeader
读取指定的报头,然后分别将它们转换成Date和int值。
vi. getHeaderNames
可以使用此方法得到一个Enumeration,枚举当前特定请求中所有的报头名称。
vii. getHeaders
大多数情况下,每个报头名称在请求中只出现一次。然而,报头偶尔也有可能出现多次,每次出现列出各自的值。可以使用此方法得到一个Enumeration,枚举报头每次出现所对应的值。
b) 获取主请求行自身的信息,同样是使用HttpServletRequest提供的方法:
i. getMethod
返回主请求方法(一般是GET或POST,也有可能是HEAD,PUT和DELETE方法)
ii. getRequestURI
返回URL中主机和端口之后,但在表单数据之前的部分。例:http://randomhost.com/servlet/search.BookSearch?subject=jsp,getRequestURI返回“/servlet/search.BookSearch”。
iii. getQueryString
返回表单数据。http://randomhost.com/servlet/search.BookSearch?subject=jsp,getQueryString返回“subject=jsp”。
iv. getProtocol
返回请求行的第三部分,一般为HTTP/1.0或HTTP/1.1。
c) 了解HTTP1.1请求报头
i. Accept:这个报头指明浏览器或其他客户程序能够处理的MIME类型。
ii. Accept-Charset:标明浏览器可以使用的字符集(如ISO-8859-1)。
iii. Accept-Encoding:详细列出客户端能够处理的编码类型。gzip或compress是二种最常见的值。
iv. Accept-Language:列出客户程序首选的语言。
v. Authorization:在访问密码保护的Web页面时,客户用这个报头来标识自己的身份。
vi. Connection:标明客户是否能够处理持续性HTTP连接。持续性连接允许客户或其他浏览器在单个socket 中读取多个文件(例如HTML文件及相关的几幅图像),从而节省协商几个独立连接所需的开销。
服务器只在读完HTTP请求之后,才会调用servlet。servlet的工作只是使服务器能够使用持续性连接;servlet通过设置Content-Length响应报头来做到这一点。
vii. Content-Length:这个报头只适用于POST请求,用来给定POST数据的大小,以字节为单位。只需简单地使用request.getContentlength()就可以得到这个报头的值。
viii. Cookie:这个报头向服务器返回cookie,这些cookie是之前由服务器发送给浏览器的。不要直接读取这个报头,而要使用requet.getCookies。
ix. Host:它标明原始URL中给出的主机名和端口号。
x. if-Modified-Since:仅当页面在指定的日期之后发生更改的情况下,客户程序才希望获取该页面。
xi. If-Unmodified-Since:这个报头和If-Modified-Since正好相反;它规定仅当文档比指定的日期要旧时,操作才需要继续。一般来说,If-Modified-Since用在GET请求中(“仅当文档比我缓存的版本要新时,才传送该文档”),而If-Unmodified-Since用在PUT请求中(“仅当我生成这个文档之后,没有其他人对它做过更改时,才更新这个文档”)。
xii. Referer:标明引用Web页面的URL。例如,如果您在Web页面1 单击指向Web页面2的链接,那么,在浏览器请求Web页面2 时,就会将Web页面1 的URL引入Referer报头。
xiii. User-Agent:标识生成请求的浏览器或其他客户程序,根据这个报头,可以针对不同类型的浏览器返回不同的内容。
第6章 服务器响应的生成:HTTP状态代码
简介:Web服务器对请求的响应,一般由一个状态行、一些响应报头、一个空行和相应的文档构成。状态行由HTTP版本、一个状态代码(整数)、以及一段对应状态代码的简短消息(如OK)组成。除MIME类型的Content-Type报头之外,其他的报头都是可选的。
1. HTTP1.1 中可用的特定状态代码。共分为5类:
a) 100-199
都是信息性的,标示客户应该采取的其他动作。
b) 200-299
表示请求成功。
c) 300-399
用于那些已经移走的文件,常常包括Location报头,指出新的地址。
d) 400-499
表明客户引发的错误。
e) 500-599
表示由服务器引发的错误。
2. 状态代码的指定:
a) 设置任意状态代码:setStatus(一个整数)建议使用HttpServletResponse中定义的常量。因为HTTP请求由状态行、一个或多个报头、一个空行、以及实际的文档按照此处开出的次序组成。所以要在用PrintWriter实际返回任何内容之前调用setStatus。
b) 设置302和404状态代码:sendRedirect和sendError
这两个方法都会抛出IOException异常,而setStatus不会。
状态代码302命令浏览器连接到新位置。sendRedirect方法生成302响应以及Location报头,给出新文档的URL。
状态代码404用于服务器没有找到文档的情况。sendError方法发送状态代码(一向为404)以及一小段简短的消息,这段消息被自动安排到HTML文档中发送给客户。
第7章 服务器响应的生成:HTTP响应报头
响应报头可以用来:指定cookie;提供页面的修改日期(用于客户端缓存),指示浏览器在指定的时间间隔后重新载入页面;给出文件的大小使持续性HTTP连接的应用成为可能;指定生成文档的类型以及执行许多其他任务。
1. 设置响应报头。
a) setHeader(String headerName, String headerValue)
这个方法将指定名称的响应报头设为给定的值。
b) setDateHeader(String header, long milliseconds)
这个方法省去将Java日期转换成GMD时间字符串的麻烦。
c) setIntHeader(String header, int headerValue)
这个方法可以省去在将整数插入到报头之前将int转换成String的不便。
HTTP允许相同的报头名多次出现,有时,我们希望加入新的报头,而非替换已有的同名报头。方法setHeader、setDateHeader、setIntHeader会替换任何同名的已有报头,而addHeader,addDateHeader和addIntHeader等方法可以添加一个报头。
d) setContentType(String mimeType)
这个方法设置Content-Type报头,大多数servlet都要用到这个方法。
e) setContentLength(int length)
这个方法设置Content-Length报头,如果浏览器支持持续性(继续使用)HTTP连接,这个报送十分有用。
f) addCookie(Cookie c)
这个方法向Set-Cookie报头插入一个cookie。由于响应中一般都会拥有多个Set-Cookie行,故而没有对应的setCookie方法。
g) sendRedirect(String address)
将状态代码设为302,同时设置Location报头。
第十章 JSP技术概述
1. 关于JSP。
我们可以将 servlet看作是含有HTML的Java代码;可以将JSP看作是含有Java代码的HTML。
a) JSP 页面仅在修改后第一次被访问时,才会被转换成servlet并进行编译;
b) 载入到内存中、初始化和执行遵循servlet的一般规则。由JSP页面生成的servlet使用_jspService方法(GET和POST请求都调用该函数),不是doGet或doPost方法。同样,对于初始化使用的是jspInit方法而非init方法。
各种情况下的JSP操作
请求次序 | 将JSP页面转换成serlvet | 编译servlet | 将servlet载入到服务器内存中 | 调用jspInit | 调用_jspService |
页面初次创建 | |||||
请求1 | 有 | 有 | 有 | 有 | 有 |
请求2 | 无 | 无 | 无 | 无 | 有 |
服务器重启后 | |||||
请求3 | 无 | 无 | 有 | 有 | 有 |
请求4 | 无 | 无 | 无 | 无 | 有 |
页面修改后 | |||||
请求5 | 有 | 有 | 有 | 有 | 有 |
请求6 | 无 | 无 | 无 | 无 | 有 |
c) JSP是一种优秀的工具,但它所处理的基本问题是表示(presentation):对于格式相对固定且含有许多静态文本的页面,JSP是一种好的选择。不太适合于结构不固定的应用;也不适合于大部分由动态数据组成的应用;对于输出二进制数据,或操作HTTP但并不生成明确输出的应用更是完全不适用。
d) servlet需要您设置CLASSPATH,使用包来避免命名冲突,将类文件安装在servlet专用的位置,同时需要使用专用的URL,而JSP页面则不需要这些。JSP页面可以和常规HTML页面、图像和样式表放在相同的目录中;还可以用与HTML页面、图像和样式表形相同的URL访问它们。(注:不允许将WEB-INF或META-INF用作目录名)
2. JSP基本语法。
a) JSP注释:<%-- Blah --%>
b) JSP表达式:<%= Java Value %>
XML语法:<jsp:expression> java Expression </jsp:expression>
c) JSP Scriptlet:<% Java Statement %>
XML语法:<jsp:scriptlet>Java Code</jsp:scriptlet>
d) JSP 声明:在页面转换成servlet时,成为类定义的一部分的字段或方法。
<%! Field Definition %>
<%! Method Dfinition %>
XML语法:<jsp:declaration>Field or Method Dfinition</jsp:declaration>
e) JSP指令:servlet代码的高层结构信息(page)、页面转换期间引入的代码(include)或所采用的定制标签库(taglib)。
<%@ directive att=”val” %>
f) JSP动作:页面被请求时应该采取的动作
<jsp:blah>…</jsp:blah>
g) JSP表达式语言的元素:简写的JSP表达式。
${ EL Expression }
h) 定制标签(定制动作)
<prefix: name> body </prefix: name>
i) 需要特殊解释的文本。使用转义序列的方式即:加“/”显示。
第十一章 用JSP脚本元素调用Java代码
1. JSP表达式的应用。
a) JSP表达式(<%= Expression %>)基本上成为由JSP页面生成的servlet中的print(或write)语句。这些print语句没有放在doGet方法中,而是放在了一个新的方法_jspService中,无论是GET和POST请求,service 方法都会调用_jspService方法。
b) 在JSP1.2及之后的版本中,只要程序设计者没有在同一页面中混合使用XML方式和标准的JSP方式(<% %>),就要求服务器支持这种语法。这意味着,要使用XML方式,那么整个页面都必须使用XML语法。
2. 编写scriptlet。如果希望完成更为复杂的任务,则可以选择使用JSP scriptlet,它可以将任意代码插入到servlet的_jspService方法中(由service方法调用)。
a) 使用scriptlet可以访问自动定义变量(request, response, session, out等)。
b) scriptlet可以完成单独使用表达式所不能完成的许多任务。由JSP生成的servlet使用Writer的一种特殊变体(JspWriter类型),它会对文档做部分缓冲。
c) scriptlet代码只是直接插入到_jspService方法中。
d) JSP表达式包含Java值(不以分号结尾),而大多数JSP scriptlet包含Java语句(必须以分号结束)。
e) scriptlet可以条件性地输出HTML或其他不在任何JSP标签内的内容。
i. scriptlet内的代码会原封不动地插入到由JSP页面生成的servlet的_jspService方法(为service方法所调用)中;
ii. scriptlet之前或之后的任何静态HTML(模板文本)会转换成print语句。(即:HTML文本可以和JSP脚本混排。
3. JSP中的自定义变量。
a) request 是与请求相关联的HttpServletRequest。可以通过它访问请求的参数、请求的类型(如GET、POST)和输入的HTTP报头(如cookie)。
b) response 是与发往客户的响应相关联的HttpServletResponse。
c) out 是用来将输出发送到客户程序的Writer。它不是标准的PrintWriter,它对输出的内容进行缓冲(JspWriter类型)。因此在JSP页面的主体内设置HTTP状态代码和响应报头一般是合法的,如果您将缓冲关闭,您必须在提供任何输出之前设置状态代码和报头。
d) session 它是与请求相关联的HttpSession对象。JSP中会话是自动创建的,因此即使不存在对输入会话的引用,也存在这个变量。
e) application 这个变量和getServletContext返回的类型相同,都为ServletContext。ServletContext由Web应用中所有的servlet和JSP页面共享。
f) config 是该页的ServletConfig对象。可以使用它来读取初始化参数,在实践中,初始化参数在jspInit中读取,而非_jspService。
g) PageContext 由JSP引入的类,通过它可以访问页面的许多属性。PageContext类拥有getRequest/getResponse/getOut/getSession等方法。pageContext变量存储与当前页面相关联的PageContext对象的值。(如果方法或构造方法需要访问多个与页面相关联的对象,传递pageContext要比传递多个对request/response/out等的独立引用更容易。
h) page 这个变量不过是this的同义词。创建它是为了在脚本语言还不是Java的时代用作点位符。
第十二章 控制所生成的servlet的结构:JSP page指令
JSP指令(directive)影响由JSP页面生成的servlet的整体结构。属性值两边要用双引号或单引号,引起来。JSP指令的通用形式。
<%directive attribute=”value” attribute1=”value1” ……attributeN=”valueN” %>
JSP中主要有3种类型的指令:
1. page指令允许我们通过类的导入、servlet超类的定制、内容类型的设置、以及诸如此类的事物来控制servlet的结构。page指令可以放在文档中的任何地方。
a) import 属性:它是page属性中惟一允许在同一文档中多次出现的属性。
<%@ page import=”package.class” %>
<%@ page import=”package.class1,…, package.classN” %>
b) contentType用来设置Content-Type响应报头,标明即将发送到客户程序的文档的MIME类型。
<%@ page contentType=”MIME-TYPE” %>
<%@ page contentType=”MIME-TYPE; charset=Character-Set” %>
在scriptlet中类似的代码有:
<% response.setContentType(“MIME-TYPE”); %>
c) 如果只想更改字符集可以使用pageEncoding=”someCharacterSet”
d) session 属性控制页面是否参与HTTP会话。可以采用下面形式:
<%@ page session=”true” %> (默认,如果存在已有会话,则预定义变量session绑定到现有的会话;否则,创建新的会话并将其绑定到session。
<%@ page session=”false” %> 表示不自动创建会话,在JSP页面转换成servlet时,对变量session的访问会导致错误。这只能关闭某个页面的会话跟踪没有任何益处,如果关闭了同一客户会话中访问到的相关页面的全部会话跟踪才有可以节省大量的服务器内存。
e) isELIgnored指定是否在页面中忽略JSP2.0表达式语言(EL),还是进行正常的求值。
<%@ page isELIgnored=”false” %> 不忽略
<%@ page isELIgnored=”true” %> 忽略EL
f) buffer指定out变量使用的缓冲区的大小。
<%@ page buffer=”size kb” %> 指定一个大小
<%@ page buffer=”none” %> 关闭缓冲
服务器实际使用的缓冲区可能比指定的更大,但不会小于指定的大小。大小至少为8KB。
g) autoFlush控制当缓冲区充满之后,是应该自动清空输出缓冲区(默认),还是在缓冲区溢出后抛出一个异常(autoFlush=”false”)。
<%@ page autoFlush=”true” %>
<%@ page autoFlush=”false” %>
当buffer=”none” 时,autoFlush的false值是不合法的。可以用取值为false来确保程序要么接收到完整的消息,要么根本没有消息。
h) info 可以在servlet中通过getServletInfo方法获取的字符串。
<%@ page info=”Some Message” %>
i) errorPage用来指定一个JSP页面,由该页面来处理当前页面中抛出但未被捕获的任何异常(即类型为Throwable的对象)。
<%@ page errorPage=”Relative URL” %>
如果要为整个Web应用指定错误页面则需要使用web.xml中的error-page元素。
j) isErrorPage 指定当前面是否可以作为其他JSP页面的错误页面。
<%@ page isErrorPage=”true” %>
<%@ page isErrorPage=”false” %> <%-- Default --%>
k) isThreadSafe控制由JSP页面生成的servlet是允许并行访问(默认),还是同一时间不允许多个访问请求访问单个serlvet实例(isThreadSafe=”false”)。
<%@ page isThreadSafe=”true” %>
<%@ page isThreadSafe=”false” %>
阻止并发访问的标准机制是实现SingleThreadModel接口。应该避免使用isThreadSafe,采用显式的同步措施取而代之。
l) extends指定JSP页面所生成的servlet的超类。
<%@ page extends=”package.class” %>
m) language 指定页面使用的脚本语言。
<%@ page language=”cobol” %>
就现在来说Java既是默认选择也是惟一合法的选择。
n) 指令的XML语法是
<jsp:directive.directiveType attribute=”value” />
例如:
<%@ page import=”java.util.*” %>
XML表示为:<jsp:directive.page import=”java.util.*” />
第十三章 在JSP 页面中包含文件和applet
2. include 指令允许我们在JSP文件转换到servlet时,将一个文件插入到JSP页面中。include指令应该放置在文档中希望插入文件的地方。
a) jsp:include 动作允许我们在请求期间将其他页面的输出包含进来。它所包含的是次级页面的输出,而非次级页面的实际代码,所以,在被包含的页面中不能含有JSP。它可以将HTML页面的内容、纯文本文档的内容、JSP页面的输出、servlet的输出。
语法:<jsp:include page=”包含的页面” flush=”是否在页面包含进来之前清空主页面的输出流” />
(注:这个动作只有XML语法)
b) jsp:param被包含页面与最初请求的页面使用相同的请求对象。如果想增加或替换这些参数,可以在<jsp:include>元素中间使用jsp:param元素(它有两项属性,name和value)。示例:
<jsp:include page=”/fragments/StandardHeading.jsp”>
<jsp:param name=”bgColor” value=”yellow” />
</jsp:include>
c) include 指令在主JSP文档转换成servlet时(一般在它首次被访问时),将文件包含到文档中。include指令允许所包含的文件中含有影响主页面的JSP代码。语法:
<%@ include file=”Relative URL” %>
XML兼容语句是:
<jsp:directive.include file=”…” />
jsp:include和include指令之间的差异
| jsp:include动作 | include指令 |
语法基本形式 | <jsp:include page=”…” /> | <%@ include file=”…” %> |
包含动作的发生时间 | 请求期间 | 页面转换期间 |
包含的内容 | 页面的输出 | 文件的实际内容 |
产生多少servlet | 两个(主页面和被包含页面都会成为独立的servlet) | 一个(被包含文件首先被插入到主页面中,然后,得到的页面被转换成servlet) |
被包含页面中可否设置影响主页面的响应报头 | 不可以 | 可以 |
被包含页面可否定义主页面使用的字段或方法 | 不可以 | 可以 |
被包含页面发生更改时是否需要更新主页面 | 不需要 | 需要 |
等同的servlet代码 | RequestDispatcher的include方法 | 没有 |
d) jsp:forward转发请求,可以获取辅助页面的完整输出。(建议使用RequestDispatcher的forward方法来完成同样的功能)。
3. jsp:plugin 元素指示服务器为使用插件的applet构建一个恰当的标签。jsp:plugin元素只是简化了OBJECT或EMBED标签的生成任务。
示例:
<jsp:plugin type=”applet” code=”SomeApplet.class” width=”300” height=”200”>
</jsp:plugin>
jsp:plugin的属性:
a) type
指定包含插件的类型,可以是applet或者是bean。
b) code
与applet的code属性相同指定applet类的文件。
c) height
与applet的height相同
d) width
与applet的height属性相同
e) codebase
与applet的codebase属性相同,指定applet的根目录。
f) align
与 applet和img的align属性相同。可以是left, right, top, bottom 和middle。
g) hspace
同applet的hspace,它指定applet左边和右边保留的空白区域,以像素为单位。
h) vspace
同applet的vspace,它指定applet上边和下边保留的空白区域。
i) archive
同applet的archive,它指定一个JAR文件,类和图像应该从这个JAR载入。
j) name
同applet的name,用于applet之间的通信,或用在脚本语言中标识这个applet。
k) title
指定工具提示或索引。
l) jreversion
指定Java运行环境的版本。默认是1.2.
m) iepluginurl
这个属性指定可以下载Internet Explorer插件的URL。
n) nspluginurl
这个属性指定可以下载Netscape插件的URL。
4. jsp:param和jsp:params元素
jsp:param元素在jsp:plugin中的应用类似于param在applet中的应用。它必须附和XML语法。它必须封闭在一对jsp:params元素内。
5. jsp:fallback引用
jsp:fallback元素向不支持OBJECT或EMBED的浏览器提供一段替换性的文字。通过使用jsp:plugin标记中。
6. taglib指令用来自定义标记标签。
第十四章 JavaBean 组件在JSP文档中的应用
1. bean是什么?
a) bean 类必须拥有一个零参数的(默认)构造函数。
b) bean类不应该有公共的实例变量(字段)
c) 持续性的值应该应该通过getXxx和setXxx方法来访问。
如果类拥有getXxx方法,但没有对应的setXxx方法,则称类拥有一个只读属性xxx。如果类有一个布尔属性:可以使用方法isXxxx检查它们的值。访问bean的标准JSP动作只能使用那些遵循getXxx/setXxx或isXxxx/setXxx命名约定的方法。
2. bean的应用:基本任务
a) jsp:useBean 这个元素最简单形式只是构建一个新的bean。常规使用方式如下:
<jsp:useBean id=”beanName” class=”package.Class” />
b) jsp:getProperty 这个元素读取或输出bean属性的值。相当于是getXxx的简单调用。常规用法如下:
<jsp:getProperty name=”beanName” property=”propertyName” />
c) jsp:setProperty 这个元素修改bean的属性(即调用如setXxx的方法)。它的常规使用方法如下:
<jsp:setProperty name=”beanName” property=”propertyName” value=”propertyValue” />
在jsp:useBean中,bean名由id属性给出。而在jsp:getProperty和jsp:setProperty中,由name属性给出。
3. 根据条件创建bean
a) 仅当找不到相同id和scope的bean时,jsp:useBean元素才会实例化新的bean。如果存在相同id和scope的bean,则只是将已有的bean绑定到相关的变量(由id指定)。
b) 可以使用
<jsp:useBean …/>
转而使用
<jsp:useBean …> statements </jsp:useBean>
使用第二种形式的意义在于,jsp:useBean的起始标签和结束标签之间的语句只在创建新的bean时执行,如果使用已有的bean,则不执行。这是模拟构造函数的一种方式。
c) jsp:useBean动作规定,仅当不存在相同的id和scope的bean时才实例化新的对象。
d) beanName 它可以指向类,也可以指定含有序列化bean对象的文件。beanName属性的值被传递给java.beans.Bean的instatiate方法。
e) type属性,您或许希望所要声明的变量的类型是实际bean为类型的超类,或是bean实现的接口。应用使用type。
救命:
<jsp:useBean id=”thread1” class=”mypackage.MyClass” type=”java.lang.Runnable” />
相当于_jspService方法中的:
java.lang.Runnable thread1 = new myPackage.MyClass();
如果这个bean已经存在,并且您只想使用现有的对象,而非创建新的对象,那么您可以只使用type,省略class。
要尽可能避免混合使用XML兼容的jsp:useBean标签和含有显式Java代码的JSP脚本元素。
4. 将bean属性与输入参数关联
a) 单个属性关联。
语法:
<jsp:setProperty name=”beanName” property=”beanProperty” param=”requestParam” />
如果请求参数和bean属性同名,则可以简化为:
<jsp:setProperty name=”beanName” property=”requestParam” />
b) 所有bean属性与请求参数关联
<jsp:setProperty name=”beanName” property=”*” />
5. 共享bean:jsp:useBean元素的scope属性决定了bean的作用域。
a) <jsp:useBean … scope=”page” />
这是scope的默认值,它表示在处理当前请求期间,将其绑定为局部变量,并放在PageContext对象中,可以使用servlet的PageContext的getAttribute方法访问它。只在当前页面有效。
b) <jsp:useBean … scope=”request” />
在处理当前请求期间,除了要将bean对象绑定到局部变量外,还将其放在HttpServletRequest中,从而可以通过getAttribute方法访问它。在使用jsp:inclue,jsp:forward,或者RequestDispatcher的include或 forward方法时,两个JSP页面,或JSP页面和servlet将会共享请求对象。
c) <jsp:useBean … scope=”session” />
除了要将bean绑定到局部变量以外,还要将它存储到与当前请求关联的HttpSession对象中,可以使用HttpSession的getAttribute方法获取bean对象。
d) <jsp:useBean … scope=”application” />
这个值表示,除了要将bean绑定到局部变量以外,还要将它存储在ServletContext中(通过预定义application变量或通过调用getSerlvetContext获得)。ServletContext由Web应用中多个servlet和JSP页面所共享。可能通过它的getAttribute方法获取。
第十五章 servlet和JSP的集成:模型-视图-控制器构架
1. 实现MVC的步骤。
a) 定义baen来表示数据。
b) 使用servlet处理请求。
c) 填写bean。
d) 将bean存储到请求、会话或servlet的上下文中。
e) 将请求转发到JSP页面。
sendRedirect需要客户连接到新的资源,而RequestDispatcher的forward方法完全在服务器上进行处理。sendredirect不自动保留所有的请求数据;而forward保留。sendRedirect产生不同的最终URL,而使用forward时维护最初servlet的URL(这个URL是相对于servlet的URL或服务器的根目录,不能相对于目的页面的实际位置。
f) 从bean中提取数据。
2. MVC代码汇总。
a) 基于请求的数据共享。
i. servlet
ValueObject value = new ValueObject(…);
request.setAttribute(“key”, value);
RequestDispatcher dispatcher =
request.getRequestDispatcher(“/WEB-INF/SomePage.jsp”);
dispatcher.forward(request, response);
ii. JSP页面。
<jsp:useBean id=”key” type=”somePackage.ValueObject” scope=”request” />
<jsp:getProperty name=”key” property=”someProperty” />
b) 基于会话的数据共享。
i. servlet
ValueObject value = new ValueObject(…);
HttpSession session = request.getSession();
session.setAttribute(“key”, value);
RequestDispatcher dispatcher =
request.getRequestDispatcher(“/WEB-INF/SomePage.jsp”);
dispatcher.forward(request, response);
ii. JSP页面
<jsp:useBean id=”key” type=”somePackage.ValueObject” scope=”session” />
<jsp:getProperty name=”key” property=”someProperty” />
c) 基于应用的数据共享。
i. servlet
synchronized (this) {
ValueObject value = new ValueObject(…);
getServletContext().setAttribute(“key”, value);
RequestDispatcher dispatcher =
request.getRequestDispatcher(“/WEB-INF/SomePage.jsp”);
dispatcher.forward(request, response);
}
ii. JSP页面
<jsp:useBean id=”key” type=”somePackage.ValueObject” scope=”application” />
<jsp:getProperty name=”key” property=”someProperty” />
第十六章 简化对Java代码的访问:JSP2.0表达式语言
为了计算和输出存储在标准位置的Java对象的值,JSP2.0引入一种简捷的语言。表达式语言(Expression Language,EL)是JSP2.0最重要的两项特性之一;另一个特性是用JSP语法(而非Java语法)定义定制标签的能力。
注:JSP表达式语言不能用在只支持JSP1.2或更早版本的服务器中。
表达式语言的语法:${expression}
1. 表达式语言支持的功能:
a) 精确地访问存储对象:
要输出“作用域变量”(用setAttribute存储在PageContext,HttpServletRequest,HttpSession或ServletContext中的对象)saleItem,我们可以使用${saleItem}。这样会实现在从小到大的范围查询这个变量的值并输出,即从PageContext到SerlvetContext。
b) bean属性的简略记法:
要输出作用域变量company的companyName属性(即getCompanyName方法的结果),我们可以使用${company.companyName}。若要访问company的president属性的firstName属性,可以使用${company.president.firstName}。
c) 对集合元素的简单访问
要访问数组、List或Map的元素,我们使用${variable[indexOrKey]}也可以写成${variable.Key}的形式。
d) 对请求参数、cookie和其它请求数据的简单访问。
i. 可以使用param访问单个请求参数,也可以使用paramValues访问请求参数数值的数组。
示例:如果访问请求参数custID可以使用以下形式
${param.custID}
ii. 访问HTTP请求报头,使用header访问单个报头,使用headerValues访问全部值。
示例:如果要访问Accept报头可以使用
${header.Accept} 或 ${header[“Accept”]}
iii. cookie
cookie对象允许我们快捷地引用输入cookie。但是,它的返回值是Cookie对象,不是cookie的值。
示例:如果访问userCookie的值
${cookie.userCookie.value}
${cookie[“userCookie”].value}
iv. 访问上下文初始化参数(context initialization parameter)使用initParam。
${iniParam.defaultColor}
v. pageScope, requestScope, sessionScope和applicationScope
这些对象允许我们限定系统应该在什么地方查找作用域变量。
示例:如果遇到 ${name} 系统会依次在PageContext, HttpServletRequest, HttpSession和 ServletContext中查找名为name的作用域变量。如果遇到如下形式 ${requestScope.name} 系统将在HttpServletRequest中查找。
e) 一组为数不多但有效的简单运算符。
i. +和-
如果任一操作数为字符串,那么,在运行时,字符串会被自动解析成数值(系统不自动捕获NumberFormatException)。如果任一操作数为BigInteger或BigDecimal类型,那么,系统会使用相应的add和subtract方法。
ii. *, / 和div
类型转换自动完成,然后进行算术的*, / 运算;/ 和 div是等同的;提供这二者是为了与Xpath和JavaScript(ECMAScript)兼容。
iii. % 和 mod
这个两运算符实现取模运算,同Java中的%。
iv. == 和 eq
相当于Java中的==运算符,也可相当于类的equals方法。
v. != 和 ne
相当于Java中的!= 运算符,也相当于类的equals方法取反。
vi. < 和 lt,> 和 gt,<= 和 le,>= 和 ge
标准的比较运算,类型转换的执行同==和!=。如果比较的操作数是字符串,则进行字面量的比较。
vii. &&,and,||,or,!和not
同标准的逻辑运算符。它们将操作数强制转换成boolean类型,并使用常规的Java“短路”运算。
viii. empty判断为空运算符。
如果这个运算符的参数为null、空字符串、空数组、空Map或空集合,则返回true。
f) 条件性输出:
?:条件运算符。
g) 自动类型转换:
h) 空值取代错误消息。
大多数情况下,没有相应的值或NullPointerExceptions异常都会导致空字符串的出现,而非抛出异常。
2. 阻止表达式语言的求值
a) 停用整个Web应用中的表达式语言:
使用指向servlet2.3(JSP1.2)或更早版本的web.xml文件完成这项任务。
servlet2.3的web.xml
<?xml version=”1.0” encoding=”ISO-8859-1” ?>
<!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN”
http://java.sun.com/dtd/web-app_2_3.dtd>
<web-app>
</web-app>
如果启用JSP表达式,可以使用下面的web.xml
<?xml version=”1.0” encoding=”ISO-8859-1” ?>
<web-app xmlns=”http://java.sun.com/xml/ns/j2ee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd”
version=”2.4”>
</web-app>
b) 停用多个JSP页面中的表达式语言:
可以使用web.xml中的jsp-property-group元素指定相应的页面。
<?xml version=”1.0” encoding=”ISO-8859-1” ?>
<web-app xmlns=”http://java.sun.com/xml/ns/j2ee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd”
version=”2.4”>
<jsp-property-group>
<url-pattern>/legacy/*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</web-app>
c) 停用个别页面中的表达式语言:
使用page指令的isELEnabled属性完成这项任务。
<%@ page isELEnabled=”false” %>
d) 停用表达式语言的个别语句。
可以将$替换为$(对应$的HTML字符实体)。也可以使用转换序列。
3. 阻止标准脚本元素的使用,在web.xml中修改。
<?xml version=”1.0” encoding=”ISO-8859-1” ?>
<web-app xmlns=”http://java.sun.com/xml/ns/j2ee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd”
version=”2.4”>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scription-invalid>true</scripting-invalid>
</jsp-property-group>
</web-app>