《Servlet与JSP核心编程》读书笔记——JSP技术

JSP技术

JSP页面需要转换成servlet,servlet在编译后,载入服务器内存中,初始化并执行,每一步在什么时候发生呢?
–JSP页面仅在修改后第一次被访问时,才会被转换成servlet并进行编译
–载入到内存中、初始化和执行遵循servlet的一般规则

模板文件的创建

JSP文档大部分由静态文本(一般是HTML)构成,称之为模板文本,为处理页面而创建的servlet只是将它们原封不动地传递给客户程序,但有两个例外:
–如果输出中含有<%或%>,需要在模板中使用<\%或%\>
–如果希望添加一段注释,使之出现在JSP页面但不出现在结果文档中,需要使用

<%-- JSP Comment --%>

下面这种形式的HTML注释按照常规的方式传递给客户程序

<!--HTML Comment -->

JSP脚本元素的类型

使用JSP脚本元素可以将Java代码插入到与JSP页面相对应的servlet中
(1)形如<%= Java Expression%>的表达式,它们在求值后插入到servlet的输出之中
(2)形如<%Java Code%>的scriptlet,它们将插入到servlet的_jspService方法(由service方法调用)中
(3)形如<%! Field/Method Declaration %>的声明,它们将插入到servlet类的定义中,不属于任何已有的方法

JSP表达式的应用

JSP表达式用来将值直接插入到输出中,它的形式如下:

<%= Java Expression %>

该表达式在求值,转换成字符串后,插入到页面中,求值是在运行期间执行

JSP scriptlet

JSP scriptlet可以将任意代码插入到servlet的_jspService方法中(由service方法调用),形式如下

<% Java Code %>

JSP表达式包含Java的值,不以分号结尾,而大多数JSP scriptlet包含Java语句,必须以分号结尾,如:

<%=bar() %>
<%bar();%>

以上JSP片段在转换为servlet时,转换成以下内容:

out.println(bar());
bar();
JSP声明

使用JSP声明可以将方法或字段的定义插入到servlet类的主定义体中(位于对请求进行处理的_jspService方法之外),声明的形式如下:

<%! Field or Method Definition %>

要注意的是不要使用JSP声明覆盖servlet的标准生命周期方法(service、doGet、init等)由JSP页面转换而成的servlet已经使用了这些方法。

预定义变量

_jspService自动定义了9个局部变量,有时也称为是“隐含对象”,他们是局部变量的名称,注意是局部变量,不是常量,也不是JSP保留字,但是如果编写的代码不属于_jspService方法,那么就不能使用这些变量。因此,JSP表达式和JSP script可以使用这些变量,但是由于JSP声明产生_jspService方法之外的代码,所以声明中不能访问这些变量。
(1)request
这个变量是与请求相关联的HttpServletRequest
(2)response
这个变量是与发往客户的响应相关联的HttpServletResponse,因为输出流(out)一般都会缓冲,所以在JSP页面的主体内设置HTTP状态代码和响应报头一般都是合法的,如果将缓冲关闭,那么就必须在提供任何输出之前设置状态代码和报头。
(3)out
这个变量是用来输出发送到客户程序的Writer,可以使用page指令的buffer属性调整缓冲区的大小。
(4)session
这个变量是与请求相关联的HttpSession对象,如果使用page指令的session属性禁用自动会话跟踪,则不存在这个变量,并且,这种情况下对session变量的引用在JSP页面转换成servlet时会引起错误。
(5)application
这个变量和getServletContext返回的类型相同,都为ServletContext,servlet和JSP页面可以在ServletContext对象中存储持续性数据。
(6)config
这个变量是该页的ServletConfig对象。
(7)pageContext
JSP引入一个PageContext对象,通过它可以访问页面许多属性,比如getRequest、getResponse、getOut等。
(8)page
表示当前页面,类似于this。
(9)exception
仅在错误页面中使用。

JSP page指令

JSP指令影响由JSP页面生成的servlet整体结构,JSP指令的两种形式:

<% directive attribute="value" %>
<% directive attribute1="value1",attribute2="value2",…attributeN="valueN"%>

属性值两边的双引号可以替换成单引号,但引号标记不能完全省略,如果要在属性值中使用引号,则要在它们之前添加反斜杠,’使用\’,”使用\”
JSP page指令控制生成的servlet的结构,包含了以下一些大小写敏感的属性:import,contentType,pageEncoding,session,isELIgnored,buffer,autoFlush,info,errorPage,isErrorPage,isThreadSafe,language和extends

import属性

page指令的import属性指定JSP页面转换成servlet应该引入的包,默认情况下servlet会引入java.lang.*,javax.servlet.*,javax.servlet.jsp.*,javax.servlet.http.*,也许还包括一些服务器特有的包(不建议使用),import属性是page属性中唯一允许在同一文档中多次出现的属性。

<%@page import="package.class"%>
<%@page import="package1.class1,…packageN.classN"%>
contentType和pageEncoding属性

contentType属性设置Content-Type响应报头,标明即将发送到客户端的文档的MIME类型和字符集

<%@page contentType="MIME-Type"%>
<%@page contentType="MIME-Type;charset=Character-Set"%>

常规的servlet默认MIME类行为text/plain,JSP页面默认的MIME类行为text/html
如果只是想更改发送到客户端文档的字符集,可以使用pageEncoding属性

<%@page pageEncoding="Page-Encoding"%>

因为指令属性的值不是运行时计算的,所以不能将page指令像模板文本一样条件性地插入到输出中,如下例:

<%if(test()) {%>
<%@page contentType="application/vnd.ms-excel%>
<%}%>

这一示例中不管test()判断true还是false,页面都会生成excel内容

session属性

session属性控制页面是否参与HTTP会话

<%@page session="true"%>默认
<%@page session="false"%>

true值表示,如果存在已有会话,则预定义变量session应该绑定到现有会话,否则,则创建新的会话并将其绑定到session。false表示不自动创建会话,在JSP页面转换成servlet时,对变量session的访问会导致错误。
需要注意的是,false并不禁用会话跟踪——它只是阻止JSP页面对那些尚不拥有会话的用户创建新的会话,关闭某个页面的会话跟踪没有任何益处,除非有可能在同一客户会话中访问到的相关页面都关闭会话跟踪。

buffer和autoFlush属性

buffer属性指定out变量使用的缓冲区大小

<%@page buffer="sizekb"%>
<%@page buffer="none"%>

如果要将缓冲区功能关闭,应该十分小心,这样做要求设置报头或状态代码的JSP元素都要出现在文件的顶部,位于任何HTML内容之前。
autoFlush属性控制当前缓冲区充满之后,使应该自动清空输出缓冲区(默认),还是在缓冲区溢出后抛出一个异常

<%@page autoFlush="true"%>
<%@page autoFlush="false"%>

buffer=”none”时,设置autoFlush=”false”是不合法的,因为没有缓冲区,也就不存在缓冲区溢出的情况

errorPage和isErrorPage属性

errorPage属性用来指定一个JSP页面,由该页面处理当前页面中抛出但未被捕获的任何异常

<%@page errorPage="Error Page URL"%>

指定的错误页面可以通过exception变量访问抛出的异常
isErrorPage属性表示当前页是否可以作为其他JSP页面的错误页面

<%@page isErrorPage="true"%>
<%@page isErrorPage="false"%>默认

在JSP页面中包含文件

jsp:include动作:jsp:include动作允许我们在请求期间将其他页面的输出包含进来,它的主要优点是:在被包含的页面发生更改时,无需对主页面做出修改。它的主要缺点是:它所包含的是次级页面的输出,而非次级页面的实际代码。
include指令:include指令可以在主页面转换成servlet之前,将JSP代码插入其中,它的主要优点是功能强大,所包含的代码可以含有总体上影响主页面的JSP构造,比如字段的定义和内容类型的设定。它的主要缺点是难以维护,只要被包含的页面发生改变,就得更新主页面。

jsp:include动作

jsp:include可以将下列内容插入到JSP的输出中:HTML页面内容,纯文本文档的内容,JSP页面的输出,servlet的输出。
jsp:include动作在主页面被请求时,将次级页面的输出包含进来,尽管被包含的页面输出中不能含有JSP,但这些页面可以是其它资源(servlet或JSP创建输出)所产生的结果。服务器可以按照正常方式对被包含资源的URL进行解释,因此,这个URL可以是servlet或者JSP页面。

page属性:指定所包含的页面

我们使用page属性指定被包含的页面

<jsp:include page="relative-path-to-resource" />

这个属性是必不可少的,它应该是指向某种资源的相对URL,如果相对URL不以斜杠/开头,则将其解释为相对于主页面的位置;以斜杠/开头的相对URL被解释为相对于Web应用的根目录(注意:不是相对于服务器的根目录)。
以斜杠/开头的URL可以记住次规则:任何时候,如果是由服务器处理它们,都按相对于当前Web应用进行解释;只有客户(浏览器)处理它们时,才会按相对于服务器的根目录来解释。
注意不要将完整的HTML文档作为被包含页面,被包含页面种只能含有适合于出现在文件插入点出的HTML标签。

flush属性

这个属性是可选的,它指定将页面包含进来之前是否应该清空主页面的输出流(默认为false)

jsp:param元素:增加请求参数

被包含页面与最初请求的页面使用相同的请求对象,因此,被包含页面看到的请求参数一般与主页面相同,如果希望增加或替换这些参数,可以使用jsp:param元素(它有两项属性,name和value)

<jsp:include page="/test/test.jsp">
    <jsp:param name="param" value="test"/>
</jsp:include>

如果jsp:param元素指定了主页面接收到的请求参数,那么由jsp:param指定的值仅在被包含文件中优先采用。

include指令

include指令可以在主JSP文档转换成servlet时(一般在它首次被访问时),将文件包含到文档中。

<%@include file="Relative URL" %>

服务器将被包含文件的内容逐字节插入到主页面中,然后将页面作为单个JSP页面进行处理

jsp:include动作include指令
包含动作的发生时间请求期间页面转换期间
包含的内容页面的输出文件的实际内容
产生多少servlet两个(主页面和被包含页面都会成为独立的servlet)一个(被包含文件首先被插入到主页面中,然后得到页面被转换成servlet)
被包含页面中可否设置影响主页面的响应报头不可以可以
被包含页面中可否定义主页面使用的字段或方法不可以可以
被包含页面更改时是否需要更新主页面不需要需要
等同的servlet代码RequestDispatcher的include方法

使用include指令,如果包含的文件发生改变,那么用到它的所有JSP页面可能都需要更新,服务器需要能够检测出JSP页面发生改变这种情况,并在处理接下来的请求之前,将它转换成新的servlet,实际上几乎没有服务器支持类似的机制,因此,大多数服务器中,包含文件发生更改时,对于所使用到的该文件的JSP文件,我们都得更新它的修改日期。
对于文件包含,应该尽可能地使用jsp:include,仅在所包含的文件中定义了主页面要用到的字段或方法,或者所包含的文件设置了主页面的响应报头时,才应该使用include指令。

JavaBean组件在JSP中的应用

bean的应用:基本任务

1、jsp:useBean
这个元素的最简单形式只是构建一个新的bean,用以载入将要用在JSP页面中的bean,如果提供scope属性,则既可以构建新的bean,也可以访问现存的bean。

<jsp:useBean id="beanName" class="package.Class"/>

这条语句类似于:实例化由class指定的类,变量的名字由id指定,其中class属性必须使用完全限定类名。
如果这个bean业已存在,并且只想使用现有的对象,而非创建新的对象,则可以只使用type属性,省略class属性。

<jsp:useBean id="beanName" type="package.Class"/>

2、jsp:getProperty
这个元素读取或输出bean属性的值,用该元素读取属性是getXx调用的简单记法。

<jsp:getProperty name="beanName" property="propertyName"/>

其中name属性应该与jsp:useBean给定的id匹配,它的property属性指定需要的属性。除了可以使用jsp:getProperty以外,还可以使用JSP表达式,显式地调用对象的方法,变量的名称由id指定。

<jsp:getProperty name="book1" property="title"/>
<%=book1.getTitile() %>

3、jsp:setProperty
这个元素修改bean的属性(即调用形如setXxx的方法)。

<jsp:setProperty name="beanName" property="propertyName" value="propertyValue" />

其中name属性与jsp:useBean的id匹配,property为要更改的属性名称,value为新的值。jsp:setProperty动作的一种替代方式是使用scriptlet显式地调用bean对象的方法。

<jsp:setProperty name="book1" property="titile" value="TestBook"/>
<% book1.setTitle("TestBook"); %>

值得注意的是,value属性允许给出的值为请求期间的表达式,如下例value值可以从请求的参数中获取。

<jsp:setProperty name="entry" property="itemID" value='<%=request.getParameter("itemID") %>' />

使用这种方法从请求参数中获取到的值默认为String类型的,如果bean中属性的类型为非String,如Integer、Double的,那么需要显式地将String类型转换为其他的基本类型,代码稍显繁琐。JSP提供一种方案,它可以将属性与请求参数相关联,自动执行从字符串到数字、字符和布尔值的类型转换,我们可以不使用value属性,而是使用param属性指定一个输入参数,被指定的请求参数的值自动用作bean属性的值,由String到基本类型的转换都自动执行。

<jsp:setProperty name="entry" property="numItems" param="numItems"/>

如果请求参数的名称和bean属性的名称相同,还可以进一步简化这段代码。

<jsp:setProperty name="entry" property="numItems"/>

但是建议最好明确地列出参数。
我们可以使用”*”作为property参数的值将bean所有的属性与同名的请求参数关联起来。系统从请求参数开始查找匹配的bean属性,而非采用相反的方式。

<jsp:setProperty name="entry" property="*"/>
共享bean

jsp:useBean创建的对象不仅是在_jspService方法中定义的一个局部变量,它还可以存储在4个不同的位置,根据jsp:useBean的可选属性scope的值来确定。scope可选的值有:page(默认),request,session和application。
1、page
page是默认值,当省略scope属性时也会得到相同的行为。page作用域表示:在处理当前请求期间,除了要将bean对象绑定到局部变量外,还应该将它放在PageContext对象中。由于每个页面和每个请求都有不同的PageContext对象,所以scope=”page”(或省略scope)表示不共享bean,也就是针对每个请求都创建新的bean。
2、request
这个值表示:当处理当前请求期间,除了要将bean对象绑定到局部变量外,还应该将它放在HttpServletRequest对象中,从而可以通过getAttribute方法访问它。
3、session
这个值表示:除了要将bean绑定到局部变量以外,还要将它存储到当前请求关联的HttpSession对象中,可以通过getAttribute获取存储在HttpSession中的对象。
4、application
这个值表示:除了要将bean绑定到局部变量以外,还要将它存储在ServletContext中,ServletContext由Web应用中多个servlet和JSP页面共享。

根据条件创建bean

两种情形下,我们可以有条件地对与bean相关的元素进行求值:
1、仅当找不到相同的id和scope的bean时,jsp:useBean元素才会实例化新的bean,如果存在相同的id和scope的bean,则只是将已有的bean绑定到相关的变量。
2、我们使用

<jsp:useBean …>statements</jsp:useBean>

jsp:useBean的起始标签和结束标签之间的语句只在创建新的bean时才执行,如果使用已有的bean,则不执行。

请求转发与请求重定向

转发请求使用RequestDispatcher的forward方法。RequestDispatcher的获取需要调用ServletRequest的getRequestDispatcher方法并提供相对地址。
请求重定向使用HttpServletRequest的sendRedirect方法。

请求转发行为汇总

1、控制的转移完全在服务器上进行,不涉及任何网络数据流。
2、用户不会看到目的JSP页面的地址,而且我们还可以将页面放在WEB-INF中,防止用户不经过建立数据的servlet,直接访问这些页面。如果JSP页面只在由servlet生成的数据的上下文中才有意义,则更应该这样做。

请求重定向行为汇总

1、控制转移通过向客户发送302状态代码和Location响应报头来完成,转移需要另外的网络往返。
2、用户能够看到目的页面的地址,并可以记下来,独立地访问。如果将JSP设计为数据缺失时使用默认值,这种方式比较适用。

请求转发与请求重定向的区别

1、请求重定向需要客户连接到新的资源,而请求转发完全在服务器上进行处理
2、请求重定向不自动保留所有的请求数据,而请求转发保留
3、请求重定向产生不同的最终URL,而请求转发维护最初servlet的URL
第3点表示,如果目的页面使用图像和样式表的相对URL,那么这些URL应该相对于servlet的URL,或服务器的根目录,不能相对目的页面的实际位置,最简单的解决方案是给出样式表文件在服务器上的完整路径。

表达式语言

表达式语言的调用

EL元素可以出现在常规文本和JSP属性标签中,只要JSP标签的属性允许常规JSP表达式。

Name:${expression1}
<jsp:include page="${expression2}"/>

在标签属性中使用表达式语言时,我们可以使用多个表达式。

<jsp:include page="${exp1}and${exp2}"/>

如果希望${出现在页面输出中,在JSP页面中需要使用\${,如果希望在EL表达式中使用单引号和双引号,则需分别使用\’和\”。

访问作用域变量

PageContext、HttpServletRequest、HttpSession和ServletContext称为作用域变量,要输出作用域变量的值,只需在表达式语言元素中使用它的名字,如:

${name}

表示在PageContext、HttpServletRequest、HttpSession和ServletContext(依照此处列出的次序)中查找名为name的属性,如果找到该属性,则调用它的toString方法返回结果,如果没有找到任何东西,则返回空字符串(不是null或错误消息),下面两个表达式是等同的:

${name}
<%=pageContext.findAttribute("name")%>

如果多个作用域中都存储了同名的属性,则表达式会按照预先定义好的次序搜索每个作用域并找到第一个属性的值。

访问bean的属性

JSP表达式语言为访问bean的属性提供了一种点号记法,如果要返回某个作用域变量customer的firstName属性,只需使用${customer.firstName}
我们也可以使用数组记法替代点号记法,如:

${name.property}

替换为:

${name["property"]}

数组记法有两项优势:
1、它允许我们在请求期间计算属性的名称,使用数组记法时,方括号内的值本身就可以是一个变量,而使用点号记法时,属性名必须是字面值。
2、数组记法允许我们使用那些不能成为合法属性名的值,在访问bean属性时,这没有任何用处,但在访问集合或请求报头时,它很有用。

访问集合

当作用域变量attributeName指向数组、List或Map时,我们可以使用下面的方式访问集合中的项:

${attributeName[entryName]}

1、如果作用域变量为数组,则entryName为索引,如下面的表达式会输出数组的第一项:

${cunstomerNames[0]}

2、如果作用域变量为实现了List接口的对象,则entryName为索引,如下面的表达式会输出List的第一项

${cunstomerNames[0]}

3、如果作用域变量实现了Map接口的对象,则entryName为键,如下面表达式会从Map中获取key为maryland的值:

${stateCapitals["maryland"]}

如果Map的键满足Java变量名的相关规定(如不能有小数点、空格和短划线等字符)则可以用点号记法来取代数组记法:

${stateCapitals.maryland}
引用隐式对象

表达式语言可以在任何JSP页面中使用,为了使得表达式语言的使用更有效率,相应的规范定义了下面的隐式对象。
1、pageContext
pageContext对象引用当前页面的PageContext,PageContext类依次拥有request,response,session,out和servletContext属性,如下面语句可以输出当前的会话ID:

${pageContext.session.id}

2、param和paramValues
这些对象允许我们访问基本的请求参数的值(param)或请求参数值的数组(paramValues),如果当前请求中不存在这个参数,则返回空字符串,而非null。
3、header和headerValues
这些对象分别访问HTTP请求报头的主要值以及全部值。
4、cookie
cookie对象允许我们快捷地引用输入cookie,但是它的返回值是Cookie对象,不是cookie的值,如果要访问cookie的值,则需要使用Cookie类标准的value属性。
5、initParam
initParam对象允许我们容易地访问上下文初始化参数。
6、pageScope,requestScope,sessionScope,applicationScope
这些对象允许我们限定系统应该在什么地方查找作用域变量,例如在遇到下面的表达式${name}时,系统会依次在PageContext,HttpServletRequest,HttpSession和ServletContext中查找名为name的作用域变量,返回所找到的第一个匹配项。如果遇到下面的表达式${requestScope.name},则系统只在HttpServletRequest中查找。

表达式语言中运算符的应用
算术运算符

1、+和-
这二者是常规的加法和减法运算符,但是有两点例外,首先,如果任一操作数为字符串,那么在运行时,字符串会被自动解析成数值(但是系统并不自动捕获NumberFormatException)。其次,如果任一操作数为BigInteger或BigDecimal类型,系统会使用相应的add和subtract方法。
2、*,/和div
它们是常规的乘法和除法运算符,但有几点例外,第一点如同+和-运算符一样,类型转换自动完成,第二,常规的算术运算符优先级依旧使用。
3、%和mod
%或与之等同的mod运算符计算模数。

关系运算符

1、==和eq
这两个相等性运算符检查参数是否相等,但是相比于Java的==运算符,他们更像Java的equals方法,如果这两个操作数为同一对象,则返回true。
2、!=和ne
这两个相等性运算符检查参数是否不同,同样相比于Java的!=运算符,它们更像Java的equalse方法的否定式。
3、<和lt,>和gt,<=和le,>=和ge
它们都是标准的算术运算符,但是有两点例外,首先,类型转换的执行同==和!=,其次,如果参数为字符串,则进行字面比较。

逻辑运算符

&&,and,||,or,!和not
它们是标准的逻辑AND,OR和NOT运算符,它们将参数强制转换成Boolean,并使用常规的Java短路求值。

空运算符

empty
如果这个运算符的参数为null,空字符串,空数组,空Map或空集合,则返回true,否则返回false。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值