JSP
JSP页面本质上是一个Servlet,JSP页面在JSP容器中运行,一个Servlet容器通常也是JSP容器。
当一个JSP页面第一次被请求时,Servlet/JSP容器主要做一下两件事情:
① 转换JSP页面到JSP页面实现类,该实现类是一个实现javax.servlet.jsp.JspPage接口或子接口javax.servlet.jsp.HttpJspPage的Java类。该实现类的类名由Servlet/JSP容器生成。如果出现转换错误,则相关错误信息将被发送到客户端。
② 如果转换成功,Servlet/JSP容器随后编译该Servlet类,并装载和实例化该类,想其他正常的Servlet一样执行生命周期操作。
用一张图来描述以上操作,如下:
对于同一个JSP页面的后续请求,Servlet/JSP容器会先检查JSP页面是否被修改过。如果是,则该JSP页面会被重新翻译、编译并执行。如果不是,则执行已经在内存中的JSP Servlet。这样一来,一个JSP页面的第一次调用的实际花费总比后来的花费多,因为它涉及翻译和编译。为了解决这个问题,可以执行下列动作:
① 配置应用程序,使所有的JSP页面在应用程序启动时被调用,而不是在第一次请求时被调用。
② 预编译JSP页面,并将其部署为Servlet。
JSP自带的API包含4个包:
包 |
说明 |
javax.servlet.jsp |
包含用于Servlet/JSP容器将JSP页面翻译为Servlet的核心类和接口。 |
javax.servlet.jsp.tagext |
包括用于开发自定义标签的类型。 |
javax.el |
提供了统一表达式语言的API。 |
javax.servlet.jsp.el |
提供了一组必须由Servlet/JSP容器支持,以便在JSP页面中使用表达式语言的类。 |
JSP支持两种不同的注释格式:
注释类型 |
格式 |
特点 |
JSP注释 |
<%-- …… --%> |
JSP注释不会被发送到浏览器端,也不会被嵌套 |
HTML/XHTML注释 |
<!-- …… --> |
一个HTML/XHTML注释不会被容器处理,会原样发送给浏览器。HTML/XHTML主食的一个用途是用来确定JSP页面本身。 |
JSP九大隐式(内置)对象:
对象 |
类型 |
说明 |
request |
javax.servlet.http.HttpServletRequest |
每当客户端请求一个JSP页面时,JSP引擎就会制造一个新的reuqest对象来代表这个请求。request对象提供了一系列方法来获取HTTP头信息、cookies、HTTP方法等等。 |
response |
javax.servlet.http.HtpServletResponse |
当服务器创建request对象时会同时创建用于响应这个客户端的response对象。response对象也定义了处理HTTP头模块的接口。通过这个对象,开发者们可以添加新的cookies、时间戳、HTTP状态码等等。 |
out |
javax.servlet.jsp.JspWriter |
out对象用来在response对象中写入内容。 |
session |
javax.servlet.http.HttpSession |
session对象用来跟踪在各个客户端请求间的会话。 |
application |
javax.servlet.ServletContext |
application对象在JSP页面的整个生命周期中都代表着这个JSP页面。这个对象在JSP页面初始化时被创建,随着jspDestory()的调用而被移除。通过向application中添加属性,则所有组成web应用的JSP文件都能访问到这些属性。 |
config |
javax.servelt.ServletConfig |
config对象允许开发者访问Servlet或者JSP引擎的初始化参数,比如文件路径等。 |
pageContext |
javax.servlet.jsp.PageContext |
pageContext对象主要用来访问页面信息,同时过滤大部分实现细节。该对象除了pageContext、page、exception对象的属性不能导出,其余内置对象的属性都能导出,而且该对象包含了传给JSP页面的指令信息,也定义了一些字段。 |
page |
javax.servlet.jsp.HttpJspPage |
page对象就是页面实例的引用,可以被看做是整个JSP页面的代表(与this对象同义) |
exception |
java.lang.Throwable |
exception对象包装了从先前页面中抛出的异常信息,通常被用来产生对出错条件的适当响应。 |
指令
指令是JSP语法元素的第一种类型。它们指示JSP转换器如何翻译JSP页面为Servlet。
page指令
可以使用page指令来控制JSP转换器转换当前JSP页面的某些方面。
page指令的语法如下:
<%@ page attribute1="value1" attribute2="value2" ...%>
以下是page指令属性的列表:
属性 |
描述 |
import |
定义一个或多个本页面中将被导入和使用的java类型。 |
session |
默认值为True,本页面加入会话管理;值为false则相反 |
buffer |
以KB为单位,定义隐式对象out的缓冲大小。必须以KB后缀结尾,默认为8KB或更大。该值可以为none,这意味着没有缓冲,所有数据将直接写入PrintWriter。 |
autoFlush |
默认值为True。若值为True,则当输出缓冲满时会自动写入输出流。而值为False,则仅当调用隐式对象的flush方法时,才会写入输出流。因此,若缓冲溢出则会抛出异常。 |
isThreadSafe |
定义该页面的线程安全级别。 |
info |
返回调用容器生成的Servlet类的getServletInfo方法的结果。 |
errorPage |
定义当出错时用来处理错误的页面。 |
isErrorPage |
标识本页是一个错误处理页面。 |
contentType |
定义本页面隐式对象response的内容类型,默认是text/html。 |
pageEncoding |
定义本页面的字符编码。 |
isELIgnored |
配置是否忽略EL表达式。 |
language |
定义本页面的脚本语言类型。 |
extends |
定义JSP实现类要继承的父类。 |
deferredSyntaxAllowedAsLiteral |
定义是否解析字符串中出现”#{“符号,默认是False。”{#”是一个表达式语言的启示符号。 |
trimDirectiveWhitespaces |
定义是否不输出多余的空格/空号,默认是False。 |
大部分page指令可以出现在页面的任何位置,但一般page指令是放在第一行代码的位置。page指令也可以出现多次,但出现多次的指令属性必须具有相同的值。不过,import属性例外,多个包含import属性的page指令的结果是累加的。
include指令
可以使用include指令将其他文件中的内容包含到当前JSP页面。一个页面中可以有多个include指令。若存在一个内容会在多个不同页面中使用或一个页面不同位置使用的场景,则将该内容模块化include文件非常有用。
include指令的语法如下:
<%@ include file="url" %>
JSP指令有很多,但是其中page和include指令最为重要,其余的taglib、tag、attribute以及variable指令会在后文出现。
脚本元素
一个脚本程序是一个Java代码块,以<%符号开始,以%>符号结束。
脚本元素类型 |
描述 |
格式 |
表达式 |
每个表达式都会被JSP容器执行,并使用隐式对象out的打印方法输出结果。 |
表达式以”<%=”开始,并以”%>”结束。表达式无需分号结尾。 |
声明 |
可以声明能在JSP页面中使用的变量和方法。可以使用声明来冲希望JSP页面,实现类的init和destory方法。 |
声明以”<%!”开始,并以”%>”结束。 |
禁用脚本元素 |
在JSP页面中用EL访问服务器端对象而不写Java代码。 |
可以通过在部署描述符中的<jsp-property-group>定义一个scripting-invalid元素,来禁用脚本元素。 <jsp-property-group> <url-pattern>*.jsp</url-pattern> <scripting-invalid>true</scripting-invalid> </jsp-property-group> |
动作
动作是第三种类型的语法元素,他们被转换成Java代码来执行操作,如访问一个Java对象或调用方法。
动作 |
说明 |
useBean |
useBean将创建一个关联Java对象的脚本变量。 |
setProperty |
setProperty动作可以对一个Java对象设置属性 |
getProperty |
getProperty动作会输出Java对象的一个属性。 |
include |
include动作用来动态地引入另一个资源。可以引入另一个JSP页面,也可以引入一个Servlet或一个静态的HTML页面。而且使用include动作可以传递参数。 |
forward |
forward将当前页面转向到其他资源。 |
错误处理
JSP提供了很好的错误处理能力。除了在Java代码中可以使用try语句,还可以指定一个特殊页面。当应用页面遇到未捕获的异常时,用户将看到一个精心设计的页面解释发生了什么,而不是一个用户无法理解的错误信息。可以使用page指令的isErrorPage属性(属性值必须为True)来标识一个JSP页面是错误页面。
EL表达式
表达式语言的语法
EL表达式以${开头,并以}结束。EL表达式的结构如下:
${expression}
它也常用来连接两个表达式。对于一系列的表达式,它们的取值将是从左到右进行,计算结果的类型是String,并且连接在一起。如果在定制标签的属性值中使用EL表达式,那么该表达式的取值结果字符串将会是强制变成该属性需要的类型:
<my:tag somAttribute="${expression}"/>
像${这样的字符顺序就表示是一个EL表达式的开头,如果需要的只是文本${,则需要在它前面加一个转义符,如\${。
关键字
以下是关键字,他们不能用作标识符:and eq gt true instanceof or ne le false empty not lt ge null div mod 。
[]和.运算符
EL表达式可以返回任意类型的值。如果EL表达式的结果是一个带有属性的对象,则可以利用[]或者.运算符来访问该属性。"[]"和"."运算符类似;"[]"是比较规范的形式,"."运算符是比较便捷的方式,不过如果该变量名(如下的propertyName)不是有效的Java变量名,那只能有用"[]"运算符。如下例子:
${object["propertyName"]}
${object.propertyName}
取值规则
EL表达式的取值是从左到右进行的。对于expr-a[expr-b]形式的表达式,其EL表达式的取值方法如下:
(1)先计算expr-a得到value-a。
(2)如果value-a为null,则返回null。
(3)然后计算expr-b得到value-b。
(4)如果value-b为null,则返回null。
(5)如果value-a为java.util.Map,则会查看value-b是否为Map中的一个key。若是,则返回value-a.get(value-b),若不是,则返回null。
(6)如果value-a为java.util.List,或者加入它是一个array,则要进行一下处理:
a.强制value-b为int,如果强制失败,则抛出异常。
b.如果value-a.get(value-b)抛出IndexOutOfBoundsException,或者加入Array.get(value-a, value-b)抛出ArrayIndexOutOfBoundsException,则返回null。
c.否则,若value-a是一个List,则返回value-a,get(value-b);若value-a是一个array,则返回Array.get(value-a, value-b)。
(7)如果value-a不是一个Map、List或者array,那么,value-a必须是一个JavaBean。在这种情况下,必须强制value-b位String。如果value-b是value-a的一个可读属性,则要调用该属性的getter方法,从中返回值。如果getter方法抛出异常,该表达式就是无效的,否则,该表达式有效。
访问JavaBean
利用"."或者"[]"运算符,都可以访问bean的属性,其结构如下:
${beanName["propertyName"]}
${beanName.propertyName}
EL隐式对象
对象 |
描述 |
pageContent |
这是当前JSP的javax.servlet.jsp.PageContent。 |
initParam |
这是一个包含所有环境初始化参数,并用参数名作为key的Map。 |
param |
这是一个包含所有请求参数,并用参数名作为key的Map。每个key的值就是指定名称的第一个参数值。因此,如果两个请求参数同名,则只有第一个能够利用param获取值。要想访问同名参数的所有参数值,就得用params代替。 |
paramValues |
这是一个包含所有请求参数,并用参数名作为key的Map。每个key的值就是一个字符串数组,其中包含了指定参数名称的所有参数值。就算该参数只有一个值,它也仍然会返回一个带有一个元素的数组。 |
header |
这是一个包含请求标题,并用标题名作为key的Map。每个key的值就是指定标题名称的第一个标题,换句话说,如果一个标题的值不止一个,则只返回第一个值。要想获得多个值的标题,得用headerValues对象代替。 |
headerValues |
这是一个包含请求标题,并用标题名作为key的Map。每个key的值就是一个字符串数组,其中包含了指定标题名称的所有参数值。就算该标题只有一个值,它也仍然会返回一个带有一个元素的数组。 |
cookie |
这是一个包含了当前请求对象中所有Cookie对象的Map。Cookie名称就是key名称,并且每个key都映射到一个Cookie对象。 |
applicationScope |
这是一个包含了ServletContext对象中所有属性的Map,并用属性名称作为key。 |
sessionScope |
这是一个包含了HttpSession对象中所有属性的Map,并用属性名称作为key。 |
requestScope |
这是一个Map,其中包含了当前HttpServletRequest对象中的所有属性,并用属性名称作为key。 |
pageScope |
这是一个Map,其中包含了全页面范围内的所有属性。属性名称就是Map的key。 |
使用其他EL运算符
①算术运算符:加法(+)、减法(-)、乘法(*)、除法(/和div)、取余/取模(%和mod)。
②逻辑运算符:和(&&和and)、或(||和or)、非(!和not)。
③关系运算符: 等于(==和eq)、不等于(!=和ne)、大于(>和gt)、大于或等于(>=和ge)、小于(<和lt)、小于或等于(<=和le)。
④empty运算符:empty运算符用来检查某一个值是否为null或者empty。如:
${empty X}
如果X为null,或者说X是一个长度为0的字符串,那么该表达式将返回True。如果是一个空Map、空数组或者空集合,它也将返回True。否则,将返回False。