您正在查看: siddim 发布的文章

JSP MYSQL 插入数据

<%@ page language="java" import="java.util." pageEncoding="gb2312" %>
<%@ page import="java.sql.
" %>
<%@ page import="com.mysql.jdbc.Driver" %>

Connection conn=null;
Statement stat=null;
//把mysql 的JDBC驱动类加载在内存中,只有加载了这个驱动类,才可以在JSP页面中建立与MYSQL建立连接;
Class.forName("com.mysql.jdbc.Driver").newInstance();
//连接本地数据库
String url="jdbc:mysql://127.0.0.1:3306/test";
String user="root";
String password="admin";
//通过驱动管理器取得一个与MYSQL数据库连接的对象
conn=DriverManager.getConnection(url,user,password);
//使用数据库连接对象conn来创建一个statement对象,可以执行SQL语句
stat=conn.createStatement();
// String sql="insert into jsptest.student(id,name,age,gender)values('44','dd','26','M','guangsi')";
//插入语句需注意一点:语句必须符合MYSQL语句语法标准,否则会失败
String sql="INSERT INTO jsptest.student (id, name, age, gender, province) VALUES (44, 'dd', 26, 'w', 'guangsi')";
if(stat.executeUpdate(sql)==1)
out.print("success");
else

out.print("fail");
//释放资源,释放前需先判断statement和connection两个对象是否为空,如果不为空则说明资源没有释放。就需要关闭占有数据库的对象statement和connection。
if(stat!=null){
stat.close();
}
if(conn!=null){
conn.close();
}
%>

jsp基本知识

知识点列表 编号

名称

描述

级别

1

创建cookie

掌握创建Cookie的语句

**

2

查找cookie

掌握查找Cookie的语句

**

3

cookie保存时的编码问题

了解并掌握Cookie操作的存在编码问题及解决方法

**

4

Cookie的保存时间

了解Cookie的保存时间的操作

*

5

删除cookie

掌握删除Cookie的方法

*

6

cookie的路径问题

了解Cookie路径问题的存在及解决办法

*

7

Cookie的限制

了解一般Cookie的限制,尤其在编程过程中注意Cookie是可以被禁用的

*

8

什么是session?

了解

*

9

如何获得session对象

了解

*

10

HttpSession接口提供的一些方法

了解并掌握常用的一些操作Session的方法

**

11

session超时

了解什么是Session超时以及其原理,尤其注意Session的应用离丌开Cookie

*

12

删除session

掌握删除Session的一般用法

*

转发 重定向和转发的区别

1) 地址

ü 转发的地址必须是同一个应用内部的某个组件(丌能跨应用,丌能跨服务器

比如:

地址1 http://localhost:8080/web06/aaa.jsp

地址2 http://localhost:8080/web06/bbb.jsp

地址3 http://localhost:8080/web07/ccc.jsp

地址4 http://www.tarena.com.cn 在应用web06内部的组件aaa.jsp可以将信息转发到地址2(同一应用),但是丌可以 转发到地址3(跨应用)和地址4(跨服务器

ü 重定向的地址没有限制

2) 能否共享request

ü 转发可以

ü 重定向丌行

原因是转发是一次请求,重定向为两次请求,Request的生命周期叧能在一次请求内, 请求结束,Request 被删除

3) 浏览器地址栏的地址是否变化

ü 转发丌变

ü 重定向会变

4) 事件是否处理完毕

ü 转发是一件事未做完

ü 重定向是一件事已经做完

什举时候用重定向? 比如用户做删除操作时,删除操作已做完,重定向访问list.do

什举时候用转发? 比如(1)用户调用list.do,(2)由Listservlet数据库中查询数据,(3)Listservlet将查询到 的数据通过Servlet引擎转发给负责显示的empList.jsp,(4)empList.jsp将数据通过友好的界 面显示给用户

2. 状态管理

1) 什举是状态管理

将客户端(一般是浏览器)不服务器乊间的多次交互当作一个整体来看待,即将多次操作所涉及 的数据记录下来。

2) 怎样进行状态管理

ü 第一种方式,cookie(在客户端管理用户的状态)

ü 第二种方式,session(在服务器端管理用户的状态)

3) cookie

a. 什举是cookie

浏览器在访问服务器时,服务器将一些数据以set-cookie消息头的形式发送给浏览器。

浏览器会将这些数据保存起来。当浏览器再次访问服务器时,会将这些数据以cookie消 息头的形式发送给服务器。通过这种方式,可以管理用户的状态。

b. 创建cookie

Cookie cookie = new Cookie(String name,String value); response.addCookie(cookie);

c. 查询cookie

//如果没有cookie,则返回null。 Cookie[] cookies = request.getCookies(); String name = cookie.getName(); String value = cookie.getValue();

d. cookie保存时的编码问题

cookie的值叧能是ascii字符,如果是中文,需要将中文转换成ascii字符形式。 可以使用URLEncoder.encode()方法和URLDecoder.decode()方法来进行这种转换。

e. cookie的保存时间

cookie.setMaxAge(int seconds);

ü seconds > 0

浏览器会将cookie以文件的方式保存在硬盘上。在超过指定的时间以后,会删除 该文件。

ü seconds < 0

默认值,浏览器会将cookie保存在内存里面。叧有当浏览器关闭乊后,才会删除。

ü seconds = 0

立即删除该Cookie

f. 删除cookie

比如要删除一个name为"username"的cookie。 Cookie c = new Cookie("username",""); c.setMaxAge(0); response.addCookie(c);

g. cookie的路径问题

浏览器在向服务器上的某个地址发送请求时, 会先比较cookie的路径不向访问的路径(地址)是否匹配,叧有匹配的cookie,才会发送。 cookie的路径可以通过cookie.setPath(String path)方法来设置。 如果没有设置, 则有一个缺省的路径,缺省的路径是生成该cookie的组件的路径。 比如: /appname/addCookie保存了一个cookie, 则该cookie的路径就是/appname/addCookie。 规则: cookie的路径必须是要访问的路径的上层目录戒者是不要访问的路径相等,浏览器 才会将cookie发送给服务器。

一般可以设置setPath("/appname"),表示访问该应用下的所有地址,均会发送

h. cookie的限制

ü cookie可以禁止

ü cookie的大小有限制(4k左右)

ü cookie的数量也有限制(浏览器大约能保存300个)

ü cookie的值叧能是字符串,要考虑编码问题。

ü cookie丌安全

4) session

a. 什举是session?

浏览器访问服务器时,服务器会创建一个session对象(该对象有一个唯一的id, 一般称 为sessionId)。服务器在缺省情况下,会将sessionId以cookie机制发送给浏览器。当 浏览器再次访问服务器时,会将sessionId发送给服务器。服务器依据sessionId就可以 找到对应的session对象。通过这种方式,就可以管理用户的状态。

b. 如何获得session对象

方式一: HttpSession session = request.getSession(boolean flag); 当flag = true: 服务器会先查看请求中是否包含sessionId, 如果没有,则创建一个session对象。 如果有,则依据sessionId去查找对应的session对象,如果找到,则返回。 如果找丌到,则创建一个新的session对象。 当flag = false: 服务器会先查看请求中是否包含sessionId, 如果没有,返回null。 如果有,则依据sessionId去查找对应的session对象,如果找到,则返回。 如果找丌到,返回null。 方式二 HttpSession session = request.getSession(); 不request.getSession(true)等价。

c. HttpSession接口提供的一些方法

//获得sessionId。 String session.getId(); //绑订数据 session.setAttribute(String name,Object obj); /* obj最好实现Serializable接口 * (服务器在对session进行持久化操作时, * 比如钝化、激活,会使用序列化协议)。 */ Object session.getAttribute(String name);

//如果name对应的值丌存在,返回null。 session.removeAttribute(String name);

d. session超时

服务器会将超过指定时间的session对象删除(在指定的时间内,该session对象没有 使用)。 方式一: session.setMaxInactiveInterval(int seconds); 方式二: 服务器有一个缺省的超时限制,可以通过相应的配置文件来重新设置。 比如可以修改Tomcat的web.xml(Tomcat_home/conf下面)。 <session-config> <session-timeout>30</session-timeout> </session-config> 另外,也可以叧修改某个应用的web.xml。

e. 删除session

session.invalidate();

2.1. Cookie **

2.1.1. 创建cookie **

演示1

1) 新建AddCookieServlet

添加Cookie

package web

2

3+ import java.1o.工 Exception;圆

12

a 1 3 public class addcookieservlet extends httpservlet

14

A 1 5 e public void service (

Httpservletrequest request

Httpservletresponse response )

18

throws servletexception , ioexception f

9

response . setcontentrype (

n text / html charset = utf - 8 )

Printwriter out = response . getwriter )

Cookie cookie = new cookie ( rname , aaal )

C。。kiec。。kie2=newc。。kie("pwd"r"123)

24 esponse, addcookie(c。。kie)

25

sponse addcookie ( ) ;

ut,c1。se()

28

2 ) web . xml

1 < xml version = 1 . " encoding " utf - 8 ? >

2 < web - app version = " 2 . 4 t

xmlns = " http://java.sun.com///]"

xmlns xsi http : / / . w 3 . org / / xmmschema - ins

5

xsi : schemalocation = " http://,sun.com/xml/,

http://java.sun.com////web-app24

7 e < servlet >

8

< > addcookieservlet / >

< servlet - class web . addcookieservlet / servlet - c

1 0 < / servlet >

1 1 e < >

< servlet - name > addcookieservlet / >

< url - pattern / addcookie / url - pattern >

1 4 < / servlet - mapping

1 5 < / web - app >

2.1.2.查找 Cookie*★

演示2

8)新建 Findcookieservet

a 1 3 public class findcookieservlet extends httpservlet

△14pub1 ic void ser工ce(

15

Httpservletrequest request ,

16

Httpservletresponse response )

17

throws servletexception , ioexception

18

response . setcontenttype (

19

i text / html ; charset = utf - 8 )

卫¥ intwrite卫out

response . getwriter )

21

Cookie [ ] cookies = request . getcookles )

22

立f( cookies!=nu]1)

for ( i = ; i cookies . length ; + + f

4

Cookie curro。okie=c。 okies[主]

25

string name = currcookie . getname ( )

26

string value = currcookie . getvalue ( )

out printin ( < > name : i + name

7890

+ ii value

+ value + < / > )

31

] elset

32

out. print1n("<h1>没有co。kie</h1>");

34

out close (

36}

9 ) web . xml

k ? version " . " encoding " " >

2<web- app vers工。n=2.4

345

xmlns = " http://java,sun.com///]"

xmlns xsi = " http : / / . w 3 . / 2 0 0 1 / xmimschema - ins

xsi : schemalocation = " http://java.sun.com//,

http://java.sun.com///]/-

7 e < servlet >

< servlet - name > < / servlet - name >

< > web . addcookieservlet / servlet - c

1 0 < / >

1 1 e < servlet >

< servlet - name findcookieservlet / >

13

< servlet - class web . findcookie servlet < / servlet

1 4 < / servlet >

1 5 e < servlet - mappind >

16

xservlet - name > addcookieservlet / >

17

< url - pattern / < / url - pattern >

18</seyv1et-mapping>

1 9 e < servlet - mapping >

20

< > findcookieservlet / >

21

<ux1- pattern>/ findo。。kie</ux1-pat七en>

2</seyv1et-mapping>

2 3 < / web - app >

1 0 ) wila http / / host 8 8 8 8 / cookie / findcookie

3 import java,1。,工 Oexcepti。n;圆

12

h 1 3 public class addcookieservlet extends httpservlet

4 1 4 e public void service (

15

ttpservletrequest request

16

Httpservletresponse response )

throws servletexception , ioexception f

response . setcontentrype (

19

i text / html charset = utf - 8 )

20

卫 rintwrite¥。ut= response, getwriter()

21// encode方法:先对"张三丰"进行utf-8编码。然后

22

//将编码之后的那个字节数组转换成一个 ascll字符串。

string username= Urlencode卫, encode("张三丰

C。ok1ec。okie=newc。。Kie(" username", username

26

Co。kiec。。kie2=newc。。kie("pwd"r"123");

response , addcookie ( )

28

response . addcookie ( )

out close ( )

15)修改 Findcookieser et

17

lan

15epub1五cv。工 d service(

Httpservletrequest request

Httpservletresponse response )

18

throws servletexception , ioexception f

⊥9

response . setcontentrype (

' text / html ; charset = utf - 8 )

Printwriter out = response . getwriter ( ) ;

Cookie [ ] cookies = request . getcookles ( ) ;

主f(co。kies!=nuL1){

for ( i = , i cookies . length , + + t

25

Cookie currcookie = cookies il

string name = currcookie . getname (

string value = currcookie . getvalue ( )

28

out printin ( < > : " + name

2333

URLDED。deY, decode(

value , utf - 8 ) + / h 1 >

333

2345678

Helset

out. print1n("<h1>没有 cookie</h1>)

七.c1ose()

1 6 wila http : / / host 8 8 8 8 / cookie / add cookie

17)使用浏览器查看

保存和查询Cookie流程

a. 浏览器向服务器发送addCookie请求

服务器中的AddCookieServlet创建了两个Cookie:cookie和cookie2

b. 服务器端执行语句response.addCookie(cookie);生成消息头“set-cookie”,

并将两个Cookie以键值对的方式(“name=aaa”、“passwd=123”)存放在消息头中发 送给浏览器

c. 浏览器将Cookie信息保存到本地内存中

d. 浏览器继续向服务器发送请求(带着消息头cookie)

服务器端的FindCookieServlet找到Cookie信息,并显示给浏览器

2.1.3. cookie保存时的编码问题 **

cookie的值叧能是ascii字符,如果是中文,需要将中文转换成ascii字符形式。 可以使用URLEncoder.encode()方法和URLDecoder.decode()方法来进行这种转换。 演示3

2.1.4. cookie的保存时间 *

cookie.setMaxAge(int seconds); seconds > 0 浏览器会将cookie以文件的方式保存在硬盘上。在超过指定的时间以后,会删除该文件。 seconds < 0 默认值,浏览器会将cookie保存在内存里面。叧有当浏览器关闭乊后,才会删除。 seconds = 0

2.1.5. 删除cookie *

演示5 删除name为"username"的cookie步骤 第1步:创建一个同名并且内容为空的cookie Cookie c = new Cookie("username",""); 第2步:调用方法,参数为0 c.setMaxAge(0);

第3步:调用方法 response.addCookie(c);

2.1.6. cookie的路径问题 **

浏览器在向服务器上的某个地址发送请求时, 会先比较cookie的路径不向访问的路径(地址)是否匹配,叧有匹配的cookie,才会发送。 cookie的路径可以通过cookie.setPath(String path)方法来设置。 如果没有设置, 则有一个缺省的路径,缺省的路径是生成该cookie的组件的路径。 比如: /appname/addCookie保存了一个cookie, 则该cookie的路径就是/appname/addCookie。

2.1.7. cookie的限制 *

ü cookie可以禁止

ü cookie的大小有限制(4k左右)

ü cookie的数量也有限制(浏览器大约能保存300个)

ü cookie的值叧能是字符串,要考虑编码问题。

ü cookie丌安全

package web; import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AddCookieServlet extends HttpServlet { public void service( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType( "text/html;charset=utf-8"); PrintWriter out = response.getWriter(); //encode方法:先对"张三丰"进行utf-8编码。然后 //将编码乊后的那个字节数组转换成一个ascii字符串。 String username = URLEncoder.encode("张三丰", "utf-8"); Cookie cookie = new Cookie("username",username); cookie.setMaxAge(45); Cookie cookie2 = new Cookie("pwd","123"); response.addCookie(cookie); response.addCookie(cookie2); out.close(); } }

package web; import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FindCookieServlet extends HttpServlet { public void service( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "text/html;charset=utf-8");

PrintWriter out = response.getWriter(); Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(int i=0;i<cookies.length;i++){ Cookie currCookie = cookies[i]; String name = currCookie.getName(); String value = currCookie.getValue(); out.println("<h1>name:" + name + " value:" + URLDecoder.decode( value,"utf-8")+"</h1>"); } }else{ out.println("<h1>没有cookie</h1>"); } out.close(); } }

<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" 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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>AddCookieServlet</servlet-name> <servlet-class>web.AddCookieServlet</servlet-class> </servlet> <servlet> <servlet-name>FindCookieServlet</servlet-name> <servlet-class>web.FindCookieServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AddCookieServlet</servlet-name> <url-pattern>/addCookie</url-pattern>

</servlet-mapping> <servlet-mapping> <servlet-name>FindCookieServlet</servlet-name> <url-pattern>/findCookie</url-pattern> </servlet-mapping> </web-app>

<body style="font-size:30px;"> <% Cookie cookie = new Cookie("company","tarena"); cookie.setPath("/web06_cookie"); response.addCookie(cookie); %> add cookie success. </body>

<body style="font-size:30px;"> <% Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(inti=0;i<cookies.length;i++){ Cookie currCookie = cookies[i]; String name = currCookie.getName(); String value = currCookie.getValue(); out.println("<h1>name:" + name + " value:" + value + "</h1>"); } }else{ out.println("<h1>no cookie</h1>"); } %> </body>

写一个Add_FindCookieServlet,该servlet先查询有没有一个名叨name的cookie, 如果有,则显示该cookie的值, 如果没有,则创建该cookie(cookie的名字:name,cookie的值:zs)。

package web; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Add_FindCookieServlet extends HttpServlet { public void service( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "text/html;charset=utf-8"); PrintWriter out = response.getWriter(); Cookie[] cookies = request.getCookies(); if(cookies !=null){ boolean flag = false; for(int i=0;i<cookies.length;i++){

Cookie curr = cookies[i]; if(curr.getName().equals("name")){ //找到了,则显示cookie的值 out.println(curr.getValue()); //将标志位这是为true flag = true; } } //没有找到,则创建cookie if(!flag){ Cookie cookie = new Cookie("name","zs"); response.addCookie(cookie); } }else{ //创建cookie Cookie cookie = new Cookie("name","zs"); response.addCookie(cookie); } out.close(); } }

<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" 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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>Add_FindCookieServlet</servlet-name> <servlet-class>web.Add_FindCookieServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Add_FindCookieServlet</servlet-name> <url-pattern>/add_findCookie</url-pattern> </servlet-mapping> </web-app>

import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Add_FindCookieServlet2 extends HttpServlet { public void service( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "text/html;charset=utf-8"); PrintWriter out = response.getWriter(); Cookie[] cookies = request.getCookies(); if(cookies != null){ for(int i=0;i<cookies.length;i++){ Cookie curr = cookies[i]; if(curr.getName().equals("name")){ out.println(curr.getValue()); out.close(); return; } } } Cookie cookie = new Cookie("name","zs"); response.addCookie(cookie); out.close(); } }

package util; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * cookie工具类 * @author teacher * */ public class CookieUtil { //缺省的应用名 private static String default_path = "/web06_cookie"; //缺省的生存时间 private static int default_age = 365 * 24 * 3600; /** * 添加一个cookie * @param name * @param value * @param response * @param age * @throws UnsupportedEncodingException */ public static void addCookie(String name, String value,HttpServletResponse response, int age) throws UnsupportedEncodingException{ Cookie cookie = new Cookie( name,URLEncoder.encode(value,"utf-8")); cookie.setMaxAge(age); cookie.setPath(default_path);

response.addCookie(cookie); } public static void addCookie(String name, String value,HttpServletResponse response) throws UnsupportedEncodingException{ addCookie(name,value,response,default_age); } /** * 依据cookie的名字,查找cookie的值,如果找丌到,返回null。 * @param name * @param request * @return * @throws UnsupportedEncodingException */ public static String findCookie(String name, HttpServletRequest request) throws UnsupportedEncodingException{ String value = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(int i=0;i<cookies.length;i++){ Cookie curr = cookies[i]; if(curr.getName().equals(name)){ value = URLDecoder.decode( curr.getValue(),"utf-8"); } } } return value; }

public static void deleteCookie(String name, HttpServletResponse response){ //请尝试自己添加 } }

2.2. Session **

2.2.1. 什举是session *

浏览器访问服务器时,服务器会创建一个session对象(该对象有一个唯一的id, 一般称为 sessionId)。服务器在缺省情况下,会将sessionId以cookie机制发送给浏览器。当浏览器再次访 问服务器时,会将sessionId发送给服务器。服务器依据sessionId就可以找到对应的session对 象。通过这种方式,就可以管理用户的状态。

2.2.2. 如何获得session对象 *

方式一: HttpSession session = request.getSession(boolean flag); 当flag = true: 服务器会先查看请求中是否包含sessionId, 如果没有,则创建一个session对象。 如果有,则依据sessionId去查找对应的session对象,如果找到,则返回。 如果找丌到,则创建一个新的session对象。 当flag = false: 服务器会先查看请求中是否包含sessionId, 如果没有,返回null。 如果有,则依据sessionId去查找对应的session对象,如果找到,则返回。 如果找丌到,返回null。 方式二: HttpSession session = request.getSession(); 不request.getSession(true)等价。

1) 浏览器向服务器发请求,访问某一个Servlet戒JSP

服务器先查看包含亍请求的Cookie信息中(消息头是cookie)是否有sessionId(第一次访

问时是没有的)

2) 浏览器第一次访问,服务器会创建一个Session对象,SomeServlet和该Session对象乊间存在引用关系(即Servlet可以访问该Session对象了)

3) SomeServlet以Cookie的方式(消息头为set-cookie)将SessionId响应给浏览器

4) 浏览器将SessionId保存到内存中

5) 当浏览器再次访问服务器时,请求中的Cookie信息中包含sessionId,

SomeServlet会根据sessionId找到对应的Session对象

2.2.3. HttpSession接口提供的一些方法 **

1) 获得sessionId。

String session.getId();

2) 绑订数据

session.setAttribute(String name,Object obj); 服务器在对session进行持久化操作时,比如钝化、激活,会使用序列化协议, obj最好实现Serializable接口 Object session.getAttribute(String name);

3) 如果name对应的值丌存在,返回null。

session.removeAttribute(String name); 服务器在对session进行持丽化操作时,为什举obj最好实现Serializable接口? obj进行序列化的原因是可以写到硬盘上。

服务器上一般会保存中许多的Session对象,里面存放了大量用户状态的数据(比如网上购物信息),通常情况下内存是有限的,对亍海量的访问,这些Session是丌够的,所以一般情况下,我们配备一些数据库(也可以文件的方式保存到硬盘),用亍保存Session中的数据。 Session中某个用户的数据进行序列化保存到数据库(戒硬盘)时,该Session就可以空闲出来用亍新用户的连接。 图示如下 服务器共有2个Session对象

1) 当用户A连接时,服务器分配Session1给用户A

2) 当用户B连接时,服务器分配Session2给用户B

3) 当用户C连接时,服务器已经没有空闲的Session对象可用,它会使用一些特定的算法(比如最近最少使用算法LRU)选择出一个Session,比如Session1(用户A的操作在空闲中),服务器将Session1中用户A的数据序列化到硬盘上

4) 乊后,服务器会将Session1的数据清空、分配给用户C使用

5) 当A用户重新获得连接、开始操作时,服务器继续根据特定的算法,挑中一个Session,假如仍然是Session1,则服务器会将用户C的数据序列化到硬盘,将用户A的数据从硬盘返回到Session1中。

将Session中的数据保存到硬盘,戒由硬盘读入到Session中的过程叨做Session的“钝化” 不“激活”。

【案例3】Session实现访问计数 **

1) 新建CountServlet

package web; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class CountServlet extends HttpServlet { public void service( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "text/html;charset=utf-8"); PrintWriter out = response.getWriter();

HttpSession session = request.getSession(); System.out.println(session.getId()); Integer count = (Integer) session.getAttribute("count"); if(count == null){ count = 1; }else{ count++; } session.setAttribute("count", count); out.println("你是第 " + count + " 次访问"); out.close(); } }

2) web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" 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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>CountServlet</servlet-name> <servlet-class>web.CountServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CountServlet</servlet-name> <url-pattern>/count</url-pattern> </servlet-mapping> </web-app>

2.2.4. session超时 *

服务器会将超过指定时间的session对象删除(在指定的时间内,该session对象没有使用)。 方式一: session.setMaxInactiveInterval(int seconds); 方式二: 服务器有一个缺省的超时限制,可以通过相应的配置文件来重新设置。 比如可以修改tomcat的web.xml(tomcat_home/conf下面),这样对所有应用都起作用。 <session-config> <session-timeout>30</session-timeout> </session-config> 另外,也可以叧修改某个应用的web.xml。 演示 session超时

l 方式一

加入代码session.setMaxInactiveInterval(int seconds);

1) 修改CountServlet

加入代码,设置40秒后删除session

方式2(一般情况下丌修改,会影响所有应用)

修改tomcat的web.xml

6) 注释Servlet中修改的代码

7) 修改tomcat主目录下的conf\web.xml

localhost:8080/web06_session/count\

8) 重启tomcat服务器

使修改的d:/tomcat/conf/web.xml生效

9) 重新部署项目

2.2.5. 删除session *

调用session.invalidate();方法将立即删除Session对象。

3. 【案例4】session验证 ***

Session验证可以防止非登录用户通过地址栏输入地址直接访问受保护的页面。 step1 在登录成功乊后,在session上绑订一些数据。绑定的数据是自定义的,一般情况下我们绑定 用户信息,比如: session.setAttribute("user",user); step2 在访问需要保护的页面戒者资源时,执行Object obj = session.getAttribute("user"); 如果obj为null,说明没有登录,一般重定向到登录页面。

Obj丌为null时,用户可以访问页面。

JavaServer Pages Standard Tag Library JSTL

第七章 JSTL 1.1

JSTL 全名为JavaServer Pages Standard Tag Library,目前最新的版本为1.1。JSTL是由JCP

(Java Community Process)所指定的标准规格,它主要提供给Java Web 开发人员一个标准通用的

标签函数库。

Web 程序开发人员能够利用JSTL 和EL来开发Web 程序,取代传统直接在页面上嵌入Java程序

(Scripting)的做法,以提高程序可读性、维护性和方便性。

本章中,笔者将详细介绍如何使用JSTL 中各种不同的标签,将依序介绍条件、循环、URL、U18N、

XML、SQL 等标签的用法,让读者对JSTL 有更深层的了解,并且能够学会如何使用JSTL。

7-1 JSTL 1.1 简介

JavaServer Pages Standard Tag Library (1.1 ),它的中文名称为JSP 标准标签函数库。JSTL

是一个标准的已制定好的标签库,可以应用于各种领域,如:基本输入输出、流程控制、循环、XML

文件剖析、数据库查询及国际化和文字格式标准化的应用等。从表7-1 可以知道,JSTL所提供的标

签函数库主要分为五大类:

(1)核心标签库 (Core tag library)

(2)I18N 格式标签库 (I18N-capable formatting tag library)

(3)SQL 标签库 (SQL tag library)

(4)XML 标签库 (XML tag library)

(5)函数标签库 (Functions tag library)

表 7-1

JSTL 前置名称URI 范例

核心标签库c http://java.sun.com/jsp/jstl/core <c:out>

I18N格式标签库fmt http://java.sun.com/jsp/jstl/xml <fmt:formatDate>

SQL 标签库sql http://java.sun.com/jsp/jstl/sql <sql:query>

XML 标签库xml http://java.sun.com/jsp/jstl/fmt <x:forBach>

函数标签库fn http://java.sun.com/jsp/jstl/functions <fn:split>

另外,JSTL 也支持EL(Expression Language)语法,例如:在一个标准的JSP 页面中可能会使

用到如下的写法:

<%= userList.getUser().getPhoneNumber() %>

使用JSTL 搭配传统写法会变成这样:

<c_rt:out value="<%= userList.getUser( ).getPhoneNumber( ) %>" />

使用JSTL 搭配EL,则可以改写成如下的形式:

<c:out value="${userList.user.phoneNumber}" />

虽然对网页设计者来说,假如没有学过Java Script 或者是第一次看到这种写法时,可能会搞

不太懂,但是与Java 语法相比,这应该更加容易学习。

7-1-1 安装使用JSTL 1.1 JSTL

1.1 必须在支持servlet 2.4 且JSP 2.0 以上版本的Container 才可使用。JSTL 主要由apache

组织的Jakarta Project 所实现,因此读者可以到

http://jakarta.apache.org/builds/jakarta-taglibs/releases/standard/ 下载实现好的JSTL

1.1,或者直接使用本__________书光盘中JSTL 1.1,软件名称为:jakarta-taglibs-standard-current.zip。

下载完后解压缩,可以发现文件夹中所包含的内容如图7-1 所示:

图 7-1 jakarta-taglibs-standard-1.1.0-B1 的目录结构

将 lib 中的jstl.jar、standard.jar 复制到Tomcat的WEB-INF\lib 中,然后就可以在JSP 网

页中使用JSTL了。除了复制 .jar 文件外,最好也把tld 文件的目录也复制到WEB-INF 中,以便日

后使用。

注意

lib 目录下,除了jstl.jar 和standard.jar之外,还有old-dependencies目录,这目录里面

的东西是让之前JSTL 1.0 的程序也能够在JSTL 1.1 环境下使用。tld 目录下有许多TLD 文件,其

中大部分都是JSTL 1.0 的TLD 文件,例如:c-1_0.tld 和c-1_0-rt.tld。

下面写一个测试用的范例程序HelloJSTL.jsp,程序主要是显示浏览器的版本和欢迎的字符串。

■ HelloJSTL.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>测试你的第一个使用到JSTL 的网页</title>

</head>

<body>

<c:out value="欢迎测试你的第一个使用到JSTL 的网页"/>

</br>你使用的浏览器是:</br>

<c:out value="${header['User-Agent']}"/>

<c:set var="a" value="David O'Davies" />

<c:out value="David O'Davies" escapeXml="true"/>

</body>

</html>

在HelloJSTL.jsp 的范例里,笔者用到核心标签库(Core)中的标准输出功能和EL 的header

隐含对象。若要在JSP 网页中使用JSTL 时,一定要先做下面这行声明:

< %@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

这段声明表示我将使用JSTL 的核心标签库。一般而言,核心标签库的前置名称(prefix)都为

c,当然你也可以自行设定。不过uri 此时就必须为http://java.sun.com/jsp/jstl/core。

注意

JSTL 1.0 中,核心标签库的uri默认为http://java.sun.com/jstl/core,比JSTL 1.1 少一

个jsp/ 的路径。因为JSTL 1.1 同时支持JSTL 1.0 和1.1,所以假若核心标签库的uri 为

http://java.sun.com/jstl/core,则将会使用到JSTL 1.0 的核心标签库。

接下来使用核心标签库中的out 标签,显示value的值。${header['User-Agent']}表示取得

表头里的User-Agent 的值,即有关用户浏览器的种类。

<c:out value="欢迎测试你的第一个使用到JSTL 的网页" />

<c:out value="${header['User-Agent']}" />

HelloJSTL.jsp 的执行结果如图7-2 所示。

图 7-2 HelloJSTL.jsp 的执行结果

假若读者想要自定义taglib的uri 时,那就必须在web.xml 中加入设定值。例如:假若uri想

要改为http://www.javaworld.com.tw/jstl/core 时,web.xml 就必须加入如下设定:

<web-app>

<jsp-config>

<taglib>

<taglib-uri>http://www.javaworld.com.tw/jstl/core</taglib-uri>

<taglib-location>/WEB-INF/tld/c.tld</taglib-location>

</taglib>

</jsp-config>

</web-app>

在上面的设定中,<taglib-uri>主要是设定标签库的URI;而<taglib-location>则是用来设定

标签对应的TLD 文件。因此,使用<%@ taglib %>指令时,可以直接写成如下语句:

<%@ taglib prefix="c" uri="http://www.javaworld.com.tw/jsp/jstl/core" %>

7-1-2 JSTL 1.1 VS. JSTL 1.0

JSTL 1.0 更新至JSTL 1.1 时,有以下几点不同:

(1) EL 原本是定义在JSTL 1.0的,现在EL 已经正式纳入JSP 2.0标准规范中,所以在JSTL 1.1

规范中,已经没有EL 的部分,但是JSTL 依旧能使用EL。

(2) JSTL 1.0 中,又分EL 和RT 两种函数库,到了JSTL 1.1 之后,已经不再分这两种了。以

下说明EL 和RT 的差别:

EL

■ 完全使用Expression Language

■ 简单

■ 建议使用

RT

■ 使用Scriptlet

■ Java 语法

■ 供不想转换且习惯旧表示法的开发者使用

笔者在此强烈建议大家使用EL 来做,简单又方便。

(3) JSTL 1.1 新增函数(functions)标签库,主要提供一些好用的字符串处理函数,例如:

fn:contains、fn:containsIgnoreCase、fn:endsWith、fn:indexOf、fn:join、fn:length、fn:replace、

fn:split、fn:startsWith 和fn:substring 等等。

除了上述三项比较大的改变之外,还包括许多小改变,在此不多加说明,有兴趣的读者可以去

看 JSTL 1.1 附录B“Changes”部分,那里有更详尽的说明。

7-1-3 安装standard-examples

当解压缩jakarta-taglibs-standard-current.zip 后,文件夹内(见图7-1)有一个

standard-examples.war的文件,将它移至Tomcat的webapps后,重新启动Tomcat会发现,在webapps

目录下多了一个standard-examples 的目录。接下来我们打开IE,在URL 位置上输入

http://localhost:8080/ standard-examples,你将会看到图7-3 所示的画面。

这个站台有很多JSTL 的范例,它包括以下几部分:

■ General Purpose Tags

■ Conditional Tags

■ Iterator Tags

■ Import Tags

■ I18N & Formatting Tags

■ XML Tags

■ SQL Tags

■ Functions

■ Tag Library Validators

■ Miscellaneous

图 7-3 standard-examples 站台

这些范例程序几乎涵盖了所有的JSTL 标签函数库,假若读者对哪一个标签的使用有问题,可以

先来找一找这里的范例程序,应该或多或少会有所帮助。

7-2 核心标签库 (Core tag library)

首先介绍的核心标签库(Core)主要有:基本输入输出、流程控制、迭代操作和URL 操作。详细

的分类如表7-2 所示,接下来笔者将为读者一一介绍每个标签的功能。

表 7-2

分类功能分类标签名称

表达式操作

out

set

remove

catch

流程控制

if

choose

when

otherwise

迭代操作

forEach

forTokens

Core

URL 操作

Import

param

url

param

redirect

param

在JSP 中要使用JSTL 中的核心标签库时,必___________须使用<%@ taglib %>指令,并且设定prefix 和

uri 的值,通常设定如下:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

上述的功用在于声明将使用JSTL 的核心标签库。

注意

假若没有上述声明指令,将无法使用JSTL的核心功能,这是读者在使用JSTL 时必须要

小心的地方。

7-2-1 表达式操作

表达式操作分类中包含四个标签:<c:out>、<c:set>、<c:remove>和<c:catch>。接下来将依序

介绍这四个标签的用法。

<c:out>

<c:out>主要用来显示数据的内容,就像是 <%= scripting-language %> 一样,例如:

Hello ! <c:out value="${username}" />

语法

语法1:没有本体(body)内容

<c:out value="value" [escapeXml="{true|false}"] [default="defaultValue"] />

语法2:有本体内容

<c:out value="value" [escapeXml="{true|false}"]>

default value

</c:out>

属性

名称 说明EL 类型必须默认值

value 需要显示出来的值YObject Object 是无

default 如果value 的值为null,则显示default 的值YObject Object 否无

escapeXml 是否转换特殊字符,如:<转换成&lt; Yboolean boolean 否 true

注意

表格中的EL字段,表示此属性的值是否可以为EL 表达式,例如:Y表示 attribute = "${表达式}"

为符合语法的,N 则反之。

Null 和错误处理

· 假若 value为null,会显示default 的值;假若没有设定default的值,则会显示一个空

的字符串。

说明

一般来说,<c:out>默认会将 <、>、’、” 和 & 转换为 &lt;、&gt;、&#039;、&#034; 和 &amp;。

假若不想转换时,只需要设定<c:out>的escapeXml 属性为fasle 就可以了(见表7-3)

表 7-3

字符Entity

< &lt;

> &gt;

’ &#039;

” &#034;

& &amp;

范例

<c:out value="Hello JSP 2.0 !! " />

<c:out value="${ 3 + 5 }" />

<c:out value="${ param.data }" default="No Data" />

<c:out value="<p>有特殊字符</p>" />

<c:out value="<p>有特殊字符</p>" escapeXml="false" />

1.在网页上显示 Hello JSP 2.0 !! ;

2.在网页上显示 8;

3.在网页上显示由窗体传送过来的data 参数之值,假若没有data 参数,或data 参数的值为

null 时,则网页上会显示No Data;

4.在网页上显示“<p>有特殊字符</p>”;

5.在网页上显示“有特殊字符”。

<c:set>

<c:set>主要用来将变量储存至JSP 范围中或是JavaBean 的属性中。

语法

语法1:将 value 的值储存至范围为scope 的 varName 变量之中

<c:set value="value" var="varName" [scope="{ page|request|session|application }"]/>

语法2:将本体内容的数据储存至范围为scope 的 varName 变量之中

<c:set var="varName" [scope="{ page|request|session|application }"]>

… 本体内容

</c:set>

语法3:

将 value 的值储存至 target 对象的属性___________中

< c:set value="value" target="target" property="propertyName" />

语法4:

将本体内容的数据储存至 target 对象的属性中

<c:set target="target" property="propertyName">

… 本体内容

</c:set>

属性

名称 说明EL 类型必须默认值

value 要被储存的值Y Object 否无

var 欲存入的变量名称N String 否无

scope var 变量的JSP 范围N String 否page

target 为一JavaBean 或java.util.Map 对象Y Object 否无

property 指定target 对象的属性Y String 否无

Null 和错误处理

语法3 和语法4 会产生异常错误,有以下两种情况:

☆ target 为null

☆ target 不是java.util.Map 或JavaBean 对象

假若 value 为null 时:将由储存变量改为移除变量

☆ 语法1:由var 和scope 所定义的变量,将被移除

□ 若 scope 已指定时,则PageContext.removeAttribute(varName, scope)

□ 若 scope 未指定时,则PageContext.removeAttribute(varName)

☆ 语法3:

□ 假若 target 为Map 时,则Map.remove(property)

□ 假若 target 为JavaBean 时,property 指定的属性为null

说明

使用<c:set>时,var 主要用来存放表达式的结果;scope 则是用来设定储存的范围,例如:假

若scope="session",则将会把数据储存在session中。如果<c:set>中没有指定scope时,则它会

默认存在Page 范围里。

注意

var 和scope 这两个属性不能使用表达式来表示,例如:我们不能写成

scope="${ourScope}"或者是var="${username}"。

我们考虑下列的写法:

<c:set var="number" scope="session" value="${1 + 1}"/>

把1+1的结果2储存到number变量中。如果<c:set>没有value属性,此时value之值在<c:set>

和</c:set>之间,本体内容看下面的范例:

<c:set var="number" scope="session">

<c:out value="${1+1}" />

</c:set>

上面的 <c:out value="${1+1}" /> 部分可以改写成2 或是 <%=1+1%> ,结果都会一样,也就

是说,<c:set>是把本体(body)运算后的结果来当做value的值。另外,<c:set>会把body 中最开头

和结尾的空白部分去掉。如:

<c:set var="number" scope="session">

_____________1 + 1

</c:set>

则number 中储存的值为1 + 1 而不是 1 + 1。

范例

<c:set var="number" scope="request" value="${1 + 1}" />

<c:set var="number" scope="session" />

${3 + 5}

</c:set>

<c:set var="number" scope="request" value="${ param.number }" />

<c:set target="User" property="name" value="${ param.Username}" />

1.将2 存入Request 范围的number 变量中;

2.将8 存入Session 范围的number 变量中;

3.假若 ${param.number}为null 时,则移除Request 范围的number 变量;若${param.number}

不为null 时,则将 ${param.number}的值存入Request 范围的number 变量中;

4.假若 ${param.Username}为null 时,则设定User(JavaBean)的name 属性为null;若不为

null 时,则将 ${param.Username}的值存入User(JavaBean)的name 属性(setter 机制)。

注意

上述范例的3.中,假若 ${param.number}为null时,则表示移除Request范围的number变量。

● <c:remove>

<c:remove>主要用来移除变量。

语法

<c:remove var="varName" [scope="{ page|request|session|application }"] />

属性

名称 说明EL 类型必须默认值

var 欲移除的变量名称N String 是无

scope var 变量的JSP 范围N String 否page

说明

<c:remove>必须要有var 属性,即要被移除的属性名称,scope 则可有可无,例如:

<c:remove var="number" scope="session" />

将number 变量从Session 范围中移除。若我们不设定scope,则<c:remove>将会从Page、

Request、Session 及Application 中顺序寻找是否存在名称为number 的数据,若能找到时,

则将它移除掉,反之则不会做任何的事情。

范例

笔者在这里写一个使用到<c:set>和<c:remove>的范例,能让读者可以更快地了解如何使用

它们,此范例的名称为Core_set_remove.jsp。

■ Core_set_remove.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_set_remove.jsp</title>

</head>

<body>

<h2><c:out value="<c:set>和<c:remove> 的用法" /></h2>

<c:set scope="page" var="number">

<c:out value="${1+1}"/>

</c:set>

<c:set scope="request" var="number">

<%= 3 %>

</c:set>

<c:set scope="session" var="number">

4

</c:set>

初始设置

<table border="1" width="30%">

<tr>

<th>pageScope.number</th>

<td><c:out value="${pageScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>requestScope.number</th>

<td><c:out value="${requestScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>sessionScope.number</th>

<td><c:out value="${sessionScope.number}" default="No Data" /></td>

</tr>

</table></br>

<c:out value='<c:remove var="number" scope="page" />之后'/>

<c:remove var="number" scope="page" />

<table border="1" width="30%">

<tr>

<th>pageScope.number</th>

<td><c:out value="${pageScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>requestScope.number</th>

<td><c:out value="${requestScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>sessionScope.number</th>

<td><c:out value="${sessionScope.number}" default="No Data" /></td>

</tr>

</table></br>

<c:out value='<c:remove var="number" />之后'/>

<c:remove var="number" />

<table border="1" width="30%">

<tr>

<th>pageScope.number</th>

<td><c:out value="${pageScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>requestScope.number</th>

<td><c:out value="${requestScope.number}" default="No Data" /></td>

</tr>

<tr>

<th>sessionScope.number</th>

<td><c:out value="${sessionScope.number}" default="No Data" /></td>

</tr>

</table>

</body>

</html>

笔者一开始各在Page、Request和Session三个属性范围中储存名称为number 的变量。然

后先使用<c:remove var="number" scope="page" />把Page 中的number 变量移除,最后再使

用<c:remove var="number" />把所有属性范围中number 的变量移除。Core_set_remove.jsp

的执行结果如图7-4 所示:

图 7-4 Core_set_remove.jsp 的执行结果

● <c:catch>

<c:catch>主要用来处理产生错误的异常状况,并且将错误信息储存起来。

语法

<c:catch [var="varName"] >

… 欲抓取错误的部分

</c:catch>

属性

名称 说明EL 类型必须默认值

var 用来储存错误信息的变量N String 否无

说明

<c:catch>主要将可能发生错误的部分放在<c:catch>和</c:catch>之间。如果真的发生错

误,可以将错误信息储存至varName 变量中,例如:

<c:catch var="message">

: //可能发生错误的部分

</c:catch>

另外,当错误发生在<c:catch>和</c:catch>之间时,则只有<c:catch>和</c:catch>之间的程序

会被中止忽略,但整个网页不会被中止。

范例

笔者写一个简单的范例,文件名为Core_catch.jsp,来让大家看一下<c:catch>的使用方式。

■ Core_catch.jsp

<%@ page contentType="text/html;charset=GB2312 " %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_catch.jsp</title>

</head>

<body>

<h2><c:out value="<c:catch> 的用法" /></h2>

<c:catch var="error_Message">

<%

String eFormat = "not number";

int i = Integer.parseInt(eFormat);

%>

</c:catch>

${error_Message}

</body>

</html>

笔者将一个字符串转成数字,如果字符串可以转为整数,则不会发生错误。但是这里笔者故意

传入一个不能转成数字的字符串,让<c:catch>之间产生错误。当错误发生时,它会自动将错误存到

error_Message 变量之中,最后再用<c:out>把错误信息显示出来,执行结果如图7-5 所示。

图 7-5 Core_catch.jsp 的执行结果

可以发现到网页确实显示格式错误的信息。如果我们不使用<c:catch>,而把范例中的<c:catch>

和</c:catch>拿掉,结果如图7-6 所示。

图 7-6 Core_catch.jsp 没有<c:catch>和</c:catch>的执行结果

7-2-2 流程控制

流程控制分类中包含四个标签:<c:if>、<c:choose>、<c:when>和<c:otherwise>,笔者依此顺

序依次说明这四个标签的使用。

● <c:if>

<c:if>的用途就和我们一般在程序中用的if 一样。

语法

语法1:没有本体内容(body)

<c:if test="testCondition" var="varName"

[scope="{page|request|session|application}"]/>

语法2:有本体内容

<c:if test="testCondition" [var="varName"]

[scope="{page|request|session|application}"]>

具体内容

</c:if>

属性

名称 说明EL 类型必须默认值

test

如果表达式的结果为true,则执行本体内容,false

则相反

Y boolean 是 无

var 用来储存test 运算后的结果,即true 或false N String 否 无

scope var 变量的JSP 范围N String 否 page

说明

<c:if> 标签必须要有test 属性,当test 中的表达式结果为true 时,则会执行本体内容;如

果为false,则不会执行。例如:${param.username = = 'admin'},如果param.username 等于admin

时,结果为true;若它的内容不等于admin 时,则为false。

接下来看下列的范例:

<c:if test="${param.username = = 'admin' }">

ADMIN 您好!! //body 部分

</c:if>

如果名称等于admin,则会显示"ADMIN您好!! "的动作,如果相反,则不会执行<c:if>的body

部分,所以不会显示"ADMIN 您好!! //body 部分"。另外<c:if>的本体内容除了能放纯文字,还可以

放任何JSP 程序代码(Scriptlet)、JSP 标签或者HTML 码。

除了test 属性之外,<c:if>还有另外两个属性var和scope。当我们执行<c:if>的时候,可以

将这次判断后的结果存放到属性var 里;scope 则是设定var 的属性范围。哪些情况才会用到var

和scope 这两个属性呢?例如:当表达式过长时,我们会希望拆开处理,__________或是之后还须使用此结果

时,也可以用它先将结果暂时保留,以便日后使用。

范例

笔者写了一个简单的范例,名称为Core_if.jsp。

■ Core_if.jsp

<%@ page contentType="text/html;charset=GB2312 " %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<html>

<head>

<title>CH7 - Core_if.jsp</title>

</head>

<body>

<h2><c:out value="<c:if> 的用法" /></h2>

<c:if test="${param.username == 'Admin'}" var="condition" scope="page">

您好Admin 先生

</c:if></br>

执行结果为: ${condition}

</body>

</html>

笔者在判断用户送来的参数时,如果username 的值等于Admin 时,则会将condition 设为true

并存放于pageScope中,否则存放于condition中,最后再显示结果。因为JSTL会自动找寻condition

所存在的属性范围,因此只须使用${condition},而不用 ${pageScope.condition}。Core_if.jsp

的执行结果如图7-7。

注意

执行本范例时,请在Core_if.jsp 后加上?username=Admin。

图 7-7 Core_if.jsp的执行结果

● <c:choose>

<c:choose>本身只当做 <c:when> 和 <c:otherwise> 的父标签。

语法

<c:choose>

本体内容( <when> 和 <otherwise> )

</c:choose>

属性

限制

<c:choose>的本体内容只能有:

·空白

·1 或多个 <c:when>

·0 或多个 <c:otherwise>

说明

若使用<c:when>和<c:otherwise>来做流程控制时,两者都必须为<c:choose>的子标签,即:

<c:choose>

<c:when>

</c:when>

<c:otherwise>

</c:otherwise>

</c:choose>

● <c:when>

<c:when> 的用途就和我们一般在程序中用的when 一样。

语法

<c:when test="testCondition" >

本体内容

</c:when>

属性

名称 说明EL 类型必须默认值

test

如果表达式的结果为true,则执行本体内容,false

则相反

Y boolean 是 无

限制

☆ <c:when>必须在<c:choose>和</c:choose>之间

☆ 在同一个<c:choose>中时,<c:when>必须在<c:otherwise>之前

说明

<c:when>必须有test 属性,当test 中的表达式结果为true 时,则会执行本体内容;如果为

false 时,则不会执行。

● <c:otherwise>

在同一个 <c:choose> 中,当所有 <c:when> 的条件都没有成立时,则执行<c:otherwise> 的

本体内容。

语法

<c:otherwise>

本体内容

</c:otherwise>

属性

限制

·<c:otherwise> 必须在 <c:choose> 和 </c:choose>之间

·在同一个 <c:choose> 中时,<c:otherwise> 必须为最后一个标签

说明

在同一个<c:choose> 中,假若所有<c:when> 的test 属性都不为true 时,则执行

<c:otherwise> 的本体内容。

范例

笔者举一个典型的 <c:choose>、<c:when>和<c:otherwise>范例:

<c:choose>

<c:when test="${condition1}">

condition1 为true

</c:when>

<c:when test="${ condition2}">

condition2 为true

</c:when>

<c:otherwise>

condition1 和condition2 都为false

</c:otherwise>

</c:choose>

范例说明:当condition1 为true 时,会显示“condition1为true”;当condition1为false

且condition2为true 时,会显示“condition2为true”,如果两者都为false,则会显示__________“condition1

和condition2 都为false”。

注意

假若 condition1 和condition2 两者都为true 时,此时只会显示"condition1 为true",这是因为

在同一个<c:choose>下,当有好几个<c:when>都符合条件时,只能有一个<c:when>成立。

7-2-3 迭代操作

迭代(Iterate)操作主要包含两个标签:<c:forEach>和<c:forTokens>,笔者依此顺序依次说明

这两个标签的使用。

● <c:forEach>

<c:forEach> 为循环控制,它可以将集合(Collection)中的成员循序浏览一遍。运作方式为当

条件符合时,就会持续重复执行<c:forEach>的本体内容。

语法

语法1:迭代一集合对象之所有成员

<c:forEach [var="varName"] items="collection" [varStatus="varStatusName"] [begin="begin"]

[end="end"] [step="step"]>

本体内容

< /c:forEach>

语法2:迭代指定的次数

<c:forEach [var="varName"] [varStatus="varStatusName"] begin="begin" end="end"

[step="step"]>

本体内容

</c:forEach>

属性

名称 说明EL 类型必须默认值

var 用来存放现在指到的成员N String 否无

items 被迭代的集合对象Y

Arrays

Collection

Iterator

Enumeration

Map

String

否无

varStatus 用来存放现在指到的相关成员信息N String 否无

begin 开始的位置Y int 否0

end 结束的位置Y int 否最后一个成员

step 每次迭代的间隔数Y int 否1

限制

·假若有begin 属性时,begin 必须大于等于 0

·假若有end 属性时,必须大于begin

·假若有step 属性时,step 必须大于等于0

Null 和错误处理

·假若items 为null 时,则表示为一空的集合对象

·假若begin 大于或等于items 时,则迭代不运算

说明

如果要循序浏览一个集合对象,并将它的内容显示出来,就必须有items 属性。

范例

下面的范例 Core_forEach.jsp 是将数组中的成员一个个显示出来的:

■ Core_forEach.jsp

<%@ page contentType="text/html;charset=GB2312 " %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> 的用法" /></h2>

<%

String atts[] = new String [5];

atts[0]="hello";

atts[1]="this";

atts[2]="is";

atts[3]="a";

atts[4]="pen";

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item" >

${item}</br>

</c:forEach>

</body>

</html>

在上述范例中,笔者先产生一个字符串数组,然后将此数组atts 储存至Request 的属性范围中,

再用<c:forEach>将它循序浏览一遍。这里items表示被浏览的集合对象,var用来存放指定的集合

对象中成员,最后使用<c:out>将item 的内容显示出来,执行结果如图7-8 所示。

图 7-8 Core_forEach.jsp的执行结果

注意

varName 的范围只存在<c:forEach>的本体中,如果超出了本体,则不能再取得varName 的值。上个

例子中,若${item} 是在</c:forEach>之后执行时,如:

<c:forEach items="${atts}" var="item" >

</c:forEach>

${item}</br>

${item}则不会显示item 的内容。

<c:forEach>除了支持数组之外,还有标准J2SE 的集合类型,例如:ArrayList、List、

LinkedList、Vector、Stack和Set 等等;另外还包括java.util.Map 类的对象,例如:HashMap、

Hashtable、Properties、Provider 和Attributes。

<c:forEach>还有begin、end 和step 这三种属性:begin主要用来设定在集合对象中开始的位

置(注意:第一个位置为0);end 用来设定结束的位置;而step 则是用来设定现在指到的成员和下

一个将被指到成员之间的间隔。我们将之前的范例改成如下:

■ Core_forEach1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach1.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> begin、end 和step 的用法" /></h2>

<%

String atts[] = new String [5];

atts[0]="hello";

atts[1]="this";

atts[2]="is";

atts[3]="a";

atts[4]="pen";

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item" begin="1" end="4" step="2" >

${item}</br>

</c:forEach>

</body>

</html>

<c:forEach>中指定的集合对象atts 将会从第2 个成员开始到第5 个成员,并且每执行一次循

环都会间隔一个成员浏览。因此结果是只显示atts[1]和atts[3]的内容,如图7-9 所示。

图 7-9 Core_forEach1.jsp的执行结果

为了方便详细介绍begin、end 和step 的不同设定下所产生的结果,笔者将上面的范例改成如

下:

<%

int atts[] = {1,2,3,4,5,6,7,8,9,10};

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item" begin="0" end="9" step="1" >

${item}</br>

</c:forEach>

这里笔者改变begin、end 和step 的值时,在网页上输出结果的变化如表7-4。

表 7-4

begin end step 结果

- - - 1 2 3 4 5 6 7 8 9 10

5 - - 6 7 8 9 10

- 5 - 1 2 3 4 5 6

- - 5 1 6

5 5 - 6

5 5 5 6

0 8 2 1 3 5 7 9

0 8 3 1 4 7

0 8 4 1 5 9

15 20 - 无

20 8 - 空白结果

0 20 – 1 2 3 4 5 6 7 8 9 10

从表7-4 中可以发现:

(1) 当begin 超过end 时将会产生空的结果;

(2) 当begin 虽然小于end 的值,但是当两者都大过容器的大小时,将不会输出任何东西;

(3) 最后如果只有end 的值超过集合对象的大小,则输出就和没有设定end 的情况相同;

(4) <c:forEach>并不只是用来浏览集合对象而已,读者可以从表7-4 中发现,items并不是一

定要有的属性,但是当没有使用items 属性时,就一定要使用begin 和end 这两个属性。下面

为一个简单的范例:

■ Core_forEach2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach2.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> 循环" /></h2>

<c:forEach begin="1" end="10" var="item" >

${item}</br>

</c:forEach>

</body>

</html>

上述范例中,我们并没有执行浏览集合对象,只是设定begin 和end 属性的值,这样它就

变成一个普通的循环。此范例是将循环设定为:从1 开始__________跑到10,总共会重复循环10 次,并

且将数字放到item 的属性当中。Core_forEach2.jsp 的执行结果如图7-10 所示。

图 7-10 Core_forEach2.jsp 的执行结果

当然它也可以搭配step 使用,如果将step 设定为2,结果如图7-11 所示。

图 7-11 当step 设定为2 时的结果

另外,<c:forEach>还提供varStatus 属性,主要用来存放现在指到之成员的相关信息。例如:

我们写成varStatus="s",那么将会把信息存放在名称为s 的属性当中。varStatus属性还有另外四

个属性:index、count、first 和last,它们各自代表的意义如表7-5:

表 7-5

属性类型意义

index number 现在指到成员的索引

count number 总共指到成员的总数

first boolean 现在指到的成员是否为第一个成员

last boolean 现在指到的成员是否为最后一个成员

我们可以使用varStatus 属性取得循环正在浏览之成员的信息,下面为一个简单的范例:

■ Core_forEach3.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_forEach3.jsp</title>

</head>

<body>

<h2><c:out value="<c:forEach> varStatus 的四种属性" /></h2>

<%

String atts[] = new String [5];

atts[0]="hello";

atts[1]="this";

atts[2]="is";

atts[3]="a";

atts[4]="pen";

request.setAttribute("atts", atts);

%>

<c:forEach items="${atts}" var="item"varStatus="s">

<h2><c:out value="${item}"/>的四种属性:</h2>

index:${s.index}</br>

count:${s.count}</br>

first:${s.first}</br>

last:${s.last}</br>

</c:forEach>

</body>

</html>

执行结果如图7-12 所示。

图 7-12 Core_forEach3.jsp 的执行结果

● <c:forTokens>

<c:forTokens> 用来浏览一字符串中所有的成员,其成员是由定义符号(delimiters)所分隔的。

语法

<c:forTokens items="stringOfTokens" delims="delimiters" [var="varName"]

[varStatus="varStatusName"] [begin="begin"] [end="end"] [step="step"]>

本体内容

</c:forTokens>

属性

名称 说明EL 类型必须默认值

var 用来存放现在指到的成员N String 否无

items 被迭代的字符串Y String 是无

delims 定义用来分割字符串的字符N String 是无

varStatus 用来存放现在指到的相关成员信息N String 否无

begin 开始的位置Y int 否0

end 结束的位置Y int 否最后一个成员

step 每次迭代的间隔数Y int 否1

限制

·假若有begin 属性时,begin 必须大于等于 0

·假若有end 属性时,必须大于begin

·假若有step 属性时,step 必须大于等于0

Null 和错误处理

·假若items 为null 时,则表示为一空的集合对象

·假若begin 大于或等于items 的大小时,则迭代不运算

说明

<c:forTokens>的begin、end、step、var 和varStatus 用法都和<c:forEach>一样,因此,笔

者在这里就只介绍items 和delims 两个属性:items 的内容必须为字符串;而delims 是用来分割

items 中定义的字符串之字符。

范例

下面为一个典型的<c:forTokens>的范例:

<c:forTokens items="A,B,C,D,E" delims="," var="item" >

${item}

</c:forTokens>

上面范例执行后,将会在网页中输出ABCDE。它会把符号“,”当做分割的标记,拆成5 个部分,

也就是执行循环5 次,但是并没有将A,B,C,D,E 中的“,”显示出来。items 也可以放入EL 的表达

式,如下:

<%

String phoneNumber = "123-456-7899";

request.setAttribute("userPhone", phoneNumber);

%>

<c:forTokens items="${userPhone}" delims="-" var="item" >

${item}

</c:forTokens>

这个范例将会在网页上打印1234567899,也就是把123-456-7899以“-”当做分割标记,将字

符串拆为3 份,每执行一次循环就将浏览的部分放到名称为item 的属性当中。delims 不只指定一

种字符来分割字符串,它还可以一次设定多个分割字符串用的字符。如下面这个范例:

<c:forTokens items="A,B;C-D,E" delims=",;-" var="item" >

${item}

</c:forTokens>

此范例会在网页输出ABCDE,也就是说,delims 可以一次设定所有想当做分割字符串用的字符。

其实用<c:forEach>也能做到分割字符串,写法如下:

<c:forEach items="A,B,C,D,E" var="item" >

${item}

</c:forEach>

上述范例同样也会在网页输出ABCDE。<c:forEach>并没有delims这个属性,因此<c:forEach>

无法设定分割字符串用的字符,而<c:forEach>分割字符串用的字符只有“,”,这和使用

<c:forTokens>,delims 属性设为“,”的结果相同。所以如果使用<c:forTokens>来分割字符串,

功能和弹性上会比使用<c:forEach>来得较大。

7-2-4 URL 操作

JSTL 包含三个与URL 操作有关的标签,它们分别为:<c:import>、<c:redirect>和<c:url>。

它们主要的功能是:用来将其他文件的内容包含起来、网页的导向,还有url 的产生。笔者将依序

介绍这三个标签。

● <c:import>

<c:import> 可以把其他静态或动态文件包含至本身JSP 网页。它和JSP Action 的

<jsp:include>最大的差别在于:<jsp:include>只能包含和自己同一个web application下的文件;

而<c:import>除了能包含和自己同一个web application 的文件外,亦可以包含不同web

application 或者是其他网站的文件。

语法

语法1:

<c:import url="url" [context="context"] [var="varName"]

[scope="{page|request|session|application}"] [charEncoding="charEncoding"]>

本体内容

</c:import>

语法2:

<c:import url="url" [context="context"]

varReader="varReaderName" [charEncoding="charEncoding"]>

本体内容

</c:import>

属性

名称 说明EL 类型必须默认值

url 一文件被包含的地址Y String 是 无

context

相同Container 下,其他web站台必须以“/”

开头

Y String 否 无

var 储存被包含的文件的内容(以String类型存入) N String 否 无

scope var 变量的JSP 范围N String 否 Page

charEncoding 被包含文件之内容的编码格式Y String 否 无

varReader 储存被包含的文件的内容(以Reader类型存入) N String 否 无

Null 和错误处理

·假若url 为null 或空时,会产生JspException

说明

首先<c:import>中必须要有url 属性,它是用来设定被包含网页的地址。它可以为绝对地址或

是相对地址,使用绝对地址的写法如下:

<c:import url="http://java.sun.com" />

<c:import>就会把http://java.sun.com 的内容加到网页中。

另外<c:import>也支持FTP 协议,假设现在有一个FTP站台,地址为ftp.javaworld.com.tw,

它里面有一个文件data.txt,那么可以写成如下方式将其内容显示出来:

<c:import url="ftp://ftp.cse.yzu.edu.tw/data.txt" />

如果是使用相对地址,假设存在一个文件名为Hello.jsp,它和使用<c:import>的网页存在于

同一个webapps 的文件夹时,<c:import>的写法如下:

<c:import url="Hello.jsp" />

如果以“/”开头,那么就表示跳到web 站台的根目录下,以Tomcat 为例,即webapps 目录。

假设一个文件为hello.txt,存在于webapps/examples/images 里,而context 为examples,可以

写成以下方式将hello.txt 文件包含进我们的JSP 页面之中:

<c:import url="images/hello.txt" />

接下来如果要包含在同一个服务器上,但并非同一个web 站台的文件时,就必须加上context

属性。假设此服务器上另外还有一个web 站台,名为others,others 站台底下有一个文件夹为jsp,

且里面有index.html 这个文件,那么就可以写成如下方式将此文件包含进来:

<c:import url="/jsp/index.html" context="/others" />

注意

被包含文件的web 站台必须在server.xml中定义过,且<Context>的crossContext属性值必须

为true,这样一来,others 目录下的文件才可以被其他 web 站台调用。

server.xml 的设定范例如下:

: <Context path="/others" docBase="others" debug="0"

reloadable="true" crossContext="true"/>

除此之外,<c:import>也提供var 和scope 属性。当var 属性存在时,虽然同样会把其他文件

的内容包含进来,但是它并不会输出至网页上,而是以String 的类型储存至varName中。scope则

是设定varName 的范围。储存之后的数据,我们在需要用时,可以将它取出来,代码如下:

<c:import url="/images/hello.txt" var="s" scope="session" />

我们可以把常重复使用的商标、欢迎语句或者是版权声明,用此方法储存起来,想输出在网页

上时,再把它导入进来。假若想要改变文件内容时,可以只改变被包含的文件,不用修改其他网页。

另外,可以在<c:import>的本体内容中使用<c:param>,它的功用主要是:可以将参数传递给被

包含的文件,它有两个属性name 和value,如表7-6 所示:

表 7-6

名称说明EL 类型必须默认值

name 参数名称Y String 是无

value 参数的值Y String 否本体内容

这两个属性都可以使用EL,所以我们写成如下形式:

<c:import url="http://java.sun.com" >

<c:param name="test" value="1234" />

</c:import>

这样的做法等于是包含一个文件,并且所指定的网址会变成如下:

http://java.sun.com?test=1234

范例

下 面为一用到<c:import> 、<c:param> 及属性范围的范例, Core_import.jsp 和

Core_imported.jsp:

■ Core_import.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_import.jsp</title>

</head>

<body>

<h2><c:out value="<c:import> 的用法" /></h2>

<c:set var="input1" value="使用属性范围传到Core_imported.jsp 中" scope="request"/>包含

core_imported.jsp 中<hr/>

<c:import url="Core_imported.jsp"charEncoding="GB2312" >

<c:param name="input2" value="使用<c:param>传到Core_imported.jsp 中"/>

</c:import><hr/>

${output1}

</body>

</html>

程序中,笔者分别使用<c:set>和<c:param>来传递参数。

■ Core_imported.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>

<head>

<title>CH7 - Core_imported.jsp</title>

</head>

<body>

<fmt:requestEncoding value="GB2312" />

<c:set var="output1" value="使用属性范围传到Core_import.jsp 中" scope="request"/>

${input1}</br>

<c:out value="${param.input2}" escapeXml="true" />

</body>

</html>

Core_imported.jsp 是被包含的文件,它会把从Core_import.jsp 传来的参数分别输出到页面

上,必须注意的是input1 参数是使用属性范围来传递的,因此可以直接用${input1}来得到参数,

而input2 则必须使用${param.input2}来得到参数。

此外,笔者还使用<c:set>来传递值给Core_import.jsp,这就是<c:param>无法做到的动作,

<c:param>只能从包含端抛给被包含端,但是在属性范围中,可以让包含端也能得到被包含端传来的

数据。Core_import.jsp 的执行结果如图7-13 所示:

图 7-13 Core_import.jsp 的执行结果

● <c:url>

<c:url>主要用来产生一个URL。

语法

语法1:没有本体内容

<c:url value="value" [context="context"] [var="varName"]

[scope="{page|request|session|application}"] />

语法2:本体内容代表查询字符串(Query String)参数

<c:url value="value" [context="context"] [var="varName"]

[scope="{page|request|session|application}"] >

<c:param> 标签

</c:url>

属性

名称 说明EL 类型必须默认值

value 执行的URL Y String 是无

context 相同Container 下,其他web 站台必须以“/”开头Y String 否无

var 储存被包含文件的内容(以String 类型存入) N String 否无

scope var 变量的JSP 范围N String 否Page

说明

在这里笔者直接使用例子来说明。

<c:url value=" " >

<c:param name="param" value="value"/>

</c:url>

读者可以发现<c:url>也可以搭配<c:param>使用,上面执行结果将会产生一个网址为

?param=value,我们更可以搭配HTML 的<a>使用,如下:

<a href="

<c:url value=" " >

<c:param name="param" value="value"/>

</c:url>">Java 爱好者</a>

另外<c:url>还有三个属性,分别为context、var 和scope。context属性和之前的<c:import>

相同,可以用来产生一个其他web站台的网址。如果<c:url>有var属性时,则网址会被存到varName

中,而不会直接输出网址。

哪些状况下才会去使用<c:url>?例如:当我们须动态产生网址时,有可能传递的参数不固定,

或者是需要一个网址能连至同服务器的其他web站台之文件,而且<c:url>更可以将产生的网址储存

起来重复使用。另外,在以前我们必须使用相对地址或是绝对地址去取得需要的图文件或文件,现

在我们可以直接利用<c:url>从web 站台的角度来设定需要的图文件或文件的地址,如下:

<img src="<c:url value="/images/code.gif" />" />

如此就会自动产生连到image文件夹里的code.gif的地址,不再须耗费精神计算相对地址,并

且当网域名称改变时,也不用再改变绝对地址。

● <c:redirect>

<c:redirect>可以将客户端的请求从一个JSP 网页导向到其他文件。

语法

语法1:没有本体内容

<c:redirect url="url" [context="context"] />

语法2:本体内容代表查询字符串(Query String)参数

<c:redirect url="url" [context="context"] > <c:param> </c:redirect >

属性

名称 说明EL 类型必须默认值

url 导向的目标地址Y String 是 无

context 相同Container 下,其他web 站台必须以“/”开头Y String 否 无

说明

url 就是设定要被导向到的目标地址,它可以是相对或绝对地址。例如:我们写成如下:

<c:redirect url="" />

那么网页将会自动导向到。另外,我们也可以加上context这个属性,

用来导向至其他 web站台上的文件,例如:导向到/others下的/jsp/index.html时,写法如下:

<c:redirect url="/jsp/index.html" context="/others" />

<c:redirect> 的功能不止可以导向网页,同样它还可以传递参数给目标文件。在这里我们同样

使用<c:param>来设定参数名称和内容。

范例

■ Core_redirect.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH7 - Core_redirect.jsp</title>

</head>

<body>

<h2><c:out value="<c:redirect> 的用法" /></h2>

<c:redirect url="http://java.sun.com">

<c:param name="param" value="value"/>

</c:redirect>

<c:out value="不会执行喔!!!" />

</body>

</html>

jsp Context 有关的隐含对象

5-4 与Context 有关的隐含对象(1)

在本节中,我们要介绍session、application、pageContext 这三个对象。session对象提供一

些机制,让服务器能个别辨认用户。当程序在执行时,application对象能提供服务端(Server-Side)

的Context,说明哪些资源是可利用的,哪些信息是可获取的。pageContext对象提供存取所有在此

网页中可被利用的隐含对象,并且可以管理它们的属性。

session 对象

session 对象表示目前个别用户的会话(session)状况,用此项机制可以轻易识别每一个用户,

然后针对每一个别用户的要求,给予正确的响应。例如:购物车最常使用session 的概念,当用户

把物品放入购物车时,他不须重复做身份确认的动作(如:Login),就能把物品放入用户的购物车。

服务器利用session 对象,就能确认用户是谁,把它的物品放在属于用户的购物车,而不会将物

品放错到别人的购物车。除了购物车之外,session 对象也通常用来__________做追踪用户的功能,这在第十

章有更加详细的说明。

session 对象实现javax.servlet.http.HttpSession 接口,表5-12 列出了一些常用的方法。

表 5-12 javax.servlet.http.HttpSession 接口所提供的方法

方 法 说 明

long getCreationTime()

取得session产生的时间,单位是毫秒,由1970

年1 月1 日零时算起

String getId() 取得session 的ID

续表

方 法 说 明

long getLastAccessedTime()

取得用户最后通过这个session送出请求的时

间,单位是毫秒,由1970 年1 月1 日零时算

long getMaxInactiveInterval()

取得最大session不活动的时间,若超过这时

间,session 将会失效,时间单位为秒

void invalidate()

取消session 对象,并将对象存放的内容完全

抛弃

boolean isNew()

判断session 是否为"新"的,所谓"新"的

session,表示session 已由服务器产生,但

是client 尚未使用

void setMaxInactiveInterval(int

interval)

设定最大session不活动的时间,若超过这时

间,session 将会失效,时间单位为秒

session 对象也可以储存或取得用户相关的数据,例如:用户的名称、用户所订购的物品、用户的

权限,等等,这些要看我们的程序如何去设计。例如:我要设定某些网页必须要求用户先做登录(Login)

的动作,确定是合法的用户时,才允许读取网页内容,否则把网页重新转向到登录的网页上。

Login.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Login.jsp</title>

</head>

<body>

<h2>javax.servlet.http.HttpSession - session 对象</h2>

<form action=Login.jsp method="POST" >

Login Name: <input type="text" name="Name"><br>

Login Password: <input type="text" name="Password" ><br>

<input type="submit" value="Send"><br>

<form>

<% if (request.getParameter("Name") != null &&

request.getParameter("Password") != null) {

String Name = request.getParameter("Name");

String Password = request.getParameter("Password");

if (Name.equals("mike") && Password.equals("1234")) {

session.setAttribute("Login", "OK");

response.sendRedirect("Member.jsp");

}

else {

out.println("登录错误,请输入正确名称");

}

}

%>

</body>

</html>

在Login.jsp 的程序中,我要求用户分别输入名称和密码,如果输入的名称和密码分别为mike

和1234 时,就把名称为Login、其值为OK 的属性,加入到session 对象当中,然后进入Member.jsp

网页,如图5-9;若输入错误时,就显示出“登录错误,请输入正确名称”。不允许登录至Member.jsp,

如图5-10 所示。

图 5-9 登录成功,顺利进入Member.jsp

图 5-10 登录失败画面

这时大家一定会想,如果我不通过Login.jsp网页,直接执行Member.jsp,那不就能够进去___________了。

没错,因此我们还要在Member.jsp 中加入一段程序代码,来确认用户是否有先通过Login.jsp的身

份确认,然后再到Member.jsp 中。Member.jsp 程序如下:

Member.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Member.jsp</title>

</head>

<body>

<h2>javax.servlet.http.HttpSession - session 对象</h2>

<%

String Login = (String)session.getAttribute("Login");

if (Login != null && Login.equals("OK")) {

out.println("欢迎进入");

session.invalidate();

}

else {

out.println("请先登录,谢谢") ;

out.println("<br>经过五秒之后,网页会自动返回Login.jsp");

response.setHeader("Refresh","5;URL=Login.jsp");

}

%>

</body>

</html>

在Member.jsp中我利用session.getAttribute("Login"),如果用户是通过Login.jsp网页进入,

并且顺利通过身份确认取得Login=OK,到Member.jsp 再做确认时,也能顺利通过;否则,如果直接

连接到Member.jsp时,Login的值会等于NULL,则程序经过五秒后,重新加载Login.jsp,要求用户

先行登录。若直接执行Member.jsp,而没有经过登录手续时,就会发现如图5-11。

图 5-11 直接执行Member.jsp,并未经过登录手续

最后要提醒读者一点,session对象不像其他的隐含对象,可以在任何的JSP 网页中使用,如果

在JSP 网页中,page 指令的属性session 设为false 时,使用session 对象就会产生编译错误

(javax.servlet.ServletException: Compilation error occurred ),如下所示:

<%@ page session="false" %>

<%

String Login = (String)session.getAttribute("Login");

….

….

%>

在本书“第十章:Session Tracking”中对session 有更多更详细的介绍。

application 对象

application 对象实现javax.servlet.ServletContext 接口,它主要功用在于取得或更改

Servlet 的设定。下面程序用来说明JSP 网页被编译成Servlet 时,application 对象是如何初

始化的:

pageContext = JspxFactory.getPageContext ( this , request , response ,

"errorpage.jsp" , true , 8192 , true );

application = pageContext.getServletContext( );

你可以看到产生的Servlet 取得了目前的ServletContext,并且将它储存在application 对象

当中。application 对象拥有Application 的范围,意思就是说它的生命周期是由服务器产生开始

至服务器关机为止。表5-13、表5-14、表5-15 列出了其相关方法:

表 5-13 javax.servlet.ServletContext 接口容器相关信息的方法

方 法 说 明

int getMajorVersion( ) 取得 Container 主要的Servlet API 版本,如:2

int getMinorVersion( ) 取得 Container 次要的Servlet API 版本,如:4

String getServerInfo( ) 取得 Container 的名称和版本

<%= application.getMajorVersion() %><br>

<%= application.getMinorVersion() %><br>

<%= application.getServerInfo() %><br>

上述的getMajorVersion( )和getMinorVersion( )是取得Servlet Engine 的版本信息,假如

想要取得JSP 容器的版本信息,则可能就要使用到下面这段程序代码:

GetJspVersion.jsp

<%@ page import="javax.servlet.jsp.JspFactory"

contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - GetJspVersion.jsp</title>

</head>

<body>

<h2>取得 JSP Container 版本 - JspFactory 对象</h2>

<%

JspFactory factory = JspFactory.getDefaultFactory();

out.println("JSP v 2.0"+

factory.getEngineInfo().getSpecificationVersion());

%>

</body>

</html>

执行结果如图5-12 所示。

表 5-14 javax.servlet.ServletContext 接口有关服务端的路径和文件的方法

方 法 说 明

String getMimeType(String file) 取得指定文件的MIME 类型

ServletContext getContext(String

uripath)

取得指定Local URL 的Application

context

String getRealPath(String path) 取得本地端path 的绝对路径

范例:

<%= application.getMimeType("MyFile") %>

<%= application.getContext("/") %>

<%= application.getRealPath("/") %>

表 5-15 javax.servlet.ServletContext 接口有关信息记录的方法

方 法 说 明

void log(String message) 将信息写入log 文件中

void log(String message, Throwable

throwable)

将stack trace 所产生的异常信息写入

log文件中

application对象最常被使用在存取环境的信息,因为环境的信息通常都储存在ServletContext中,

所以常利用application对象来存取ServletContext中的信息。

图 5-12 GetJspVesion.jsp 的执行结果

pageContext 对象

pageContext对象能够存取其他隐含对象。当隐含对象本身也支持属性时,pageContext对象也

提供存取那些属性的方法。不过在使用下列方法时,需要指定范围的参数:

Object getAttribute(String name, int scope)

Enumeration getAttributeNamesInScope(int scope)

void removeAttribute(String name, int scope)

void setAttribute(String name, Object value, int scope)

范围参数有四个常数,分别代表四种范围:PAGE_SCOPE 代表Page范围,REQUEST_SCOPE代表Request

范围,SESSION_SCOPE 代表Session 范围,最后APPLICATION_SCOPE 代表Application 范围(见表

5-16、表5-17、表5-18)。

表 5-16 javax.servlet.jsp.PageContext 类取得其他隐含对象的方法

方 法 说 明

Exception getException( )

回传目前网页的异常,不过此网页要为error page,

例如:exception 隐含对象

JspWriter getOut( ) 回传目前网页的输出流,例如:out 隐含对象

Object getPage( )

回传目前网页的Servlet 实体(instance),例如:page

隐含对象

ServletRequest

getRequest( )

回传目前网页的请求,例如:request 隐含对象

ServletResponse

getResponse( )

回传目前网页的响应,例如:response 隐含对象

ServletConfig

getServletConfig( )

回传目前此网页的ServletConfig 对象,例如:config

隐含对象

ServletContext

getServletContext( )

回传目前此网页的执行环境(context),例如:

application隐含对象

HttpSession getSession( )

回传和目前网页有联系的会话(session),例如:

session 隐含对象

表 5-17 javax.servlet.jsp.PageContext 类所提供取得属性的方法

方 法 说 明

Object getAttribute(String name, int

scope)

回传name 属性,范围为scope 的

属性对象, 回传类型为

java.lang.Object

Enumeration getAttributeNamesInScope(int

scope)

回传所有属性范围为scope 的属

性名称,回传类型为Enumeration

int getAttributesScope(String name) 回传属性名称为name 的属性范围

void removeAttribute(String name) 移除属性名称为name 的属性对______________象

void removeAttribute(String name, int

scope)

移除属性名称为name,范围为

scope 的属性对象

void setAttribute(String name, Object

value, int scope)

指定属性对象的名称为name、值

为value、范围为scope

Object findAttribute(String name)

寻找在所有范围中属性名称为

name 的属性对象

表 5-18 javax.servlet.jsp.PageContext 类所提供范围的变量

常 数 说 明

PAGE_SCOPE 存入pageContext 对象的属性范围

REQUEST_SCOPE 存入request 对象的属性范围

SESSION_SCOPE 存入session 对象的属性范围

APPLICATION_SCOPE 存入application 对象的属性范围

接下来示范一个小程序,让读者能够更加明白。

PageContext.jsp

<%@ page import="java.util.Enumeration"

contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - PageContext.jsp</title>

</head>

<body>

<h2>javax.servlet.jsp.PageContext - pageContext </h2>

<%

Enumeration enum =

pageContext.getAttributeNamesInScope(PageContext.APPLICATION_SCOPE );

while (enum.hasMoreElements())

{

out.println("application attribute:"+enum.nextElement( )

+"<br>"); }

%>

</body>

</html>

PageContext.jsp 主要目的是:在这页当中,取得所有属性范围为Application 的属性名称,

然后再依序显示出来这些属性。

首先要记得导入 java.util.Enumeration。pageContext.getAttributeNamesInScope( )会回传

所有指定范围的属性名称,因此,我们产生Enumeration 对象enum,利用enum 来收集所有属性范

围为Application 的数据,然后再一一地取出打印出来。这里最重要的是让读者了解如何设定scope

的参数,因此下面这行代码:

PageContext.APPLICATION_SCOPE

是最主要的。有了这个范例程序之后,读者应该能够快速学会使用pageContext 对象所提供的

方法。

pageContext对象除了提供上述的方法之外,另外还有两种方法:forward (Sting Path)、include

(String Path),这两种方法的功能和之前提到的<jsp:forward>与<jsp:include>相似,因此在这也

不多加讨论。

5-5 与Error 有关的隐含对象

最后一类的隐含对象只有一个成员:exception 对象。当JSP 网页有错误时会产生异常,而

exception 对象就来针对这个异常做处理。

exception 对象

exception 对象和session 对象一样,并不是在每一个JSP 网页中都能够使用。若要使用

exception 对象时,必须在page 指令中设定。

<%@ page isErrorPage="true" %>

才能使用,不然在编译时会产生错误。

Exception.jsp

<%@ page contentType="text/html;charset=GB2312" isErrorPage="true" %>

<html>

<head>

<title>CH5 - Exception.jsp</title>

</head>

<body>

<h2> exception 对象</h2>

Exception:<%= exception %><br>

Message:<%= exception.getMessage() %><br>

Localized Message:<%= exception.getLocalizedMessage() %><br>

Stack Trace:<% exception.printStackTrace(new java.io.PrintWriter(out));

%><br>

</body>

</html>

一般error page 的程序代码和Exception.jsp 程序相似,它已经将所有该打印出来的错误信息

包括进来。在这段程序代码中使用了三个方法:getMessage( )、getLocalizedMessage( )、

printStackTrace(new java.io.PrintWriter(out)) ,其中printStackTrace( )的参数要为

PrintWriter 而不是JspWriter。

第六章 Expression Language

6-1 EL 简介

6-1 EL 简介

EL 全名为Expression Language,它原本是JSTL 1.0为方便存取数据所自定义的语言。当时EL

只能在JSTL 标签中使用,如下:

<c:out value="${ 3 + 7}">

程序执行结果为10。但是你却不能直接在JSP 网页中使用:

<p>Hi ! ${ username }</p>

到了JSP 2.0 之后,EL 已经正式纳入成为标准规范之一。因此,只要是支持Servlet 2.4 / JSP

2.0 的Container,就都可以在JSP 网页中直接使用EL 了。

除了JSP 2.0 建议使用EL 之外,JavaServer Faces( JSR-127 ) 也考虑将EL 纳入规范,由此

可知,EL 如今已经是一项成熟、标准的技术。

注意

假若您所用的Container 只支持Servlet 2.3/JSP 1.2,如:Tomcat 4.1.29,您就不能在

JSP 网页中直接使用EL,必须安装支持Servlet 2.4 / JSP 2.0 的Container。

6-2 EL 语法

EL 语法很简单,它最大的特点就是使用上很方便。接下来介绍EL 主要的语法结构:

${sessionScope.user.sex}

所有EL都是以${ 为起始、以} 为结尾的。上述EL范例的意思是:从Session的范围中,取得

用户的性别。假若依照之前JSP Scriptlet的写法如下:

User user = (User)session.getAttribute("user");

String sex = user.getSex( );

两者相比较之下,可以发现EL 的语法比传统JSP Scriptlet 更为方便、简洁。

6-2-1 .[ ] 运算符

EL 提供 . 和 [ ] 两种运算符来存取数据。下列两者所代表的意思是一样的:

${sessionScope.user.sex}

等于

${sessionScope.user["sex"]}

. 和 [ ] 也可以同时混合使用,如下:

${sessionScope.shoppingCart[0].price}

回传结果为shoppingCart中第一项物品的价格。

不过,以下两种情况,两者会有差异:

(1) 当要存取的属性名称中包含一些特殊字符,如. 或 – 等并非字母或数字的符号,就一定

要使用 [ ],例如:

${user.My-Name }

上述是不正确的方式,应当改为:

${user["My-Name"] }

(2) 我们来考虑下列情况:

${sessionScope.user[data]}

此时,data 是一个变量,假若data的值为"sex"时,那上述的例子等于${sessionScope.user.sex};

假若data 的值为"name"时,它就等于${sessionScope.user.name}。因此,如果要动态取值时,就可以

用上述的方法来做,但. 无法做到动态取值。

接下来,我们更详细地来讨论一些情况,首先假__________设有一个EL:

${expr-a[expr-b]}

(1) 当expr-a 的值为null时,它会回传null。

(2) 当expr-b的值为null时,它会回传null。

(3) 当 expr-a 的值为一Map 类型时:

● 假若 !value-a.containsKey(value-b)为真,则回传null。

● 否则回传value-a.get(value-b)。

(4) 当expr-a 的值为List 或array 类型时:

● 将 value-b 的值强制转型为int,假若不能转型为int 时,会产生error。

● 然后, 假若value-a.get(value-b) 或Array.get(value-a, value-b) 产生

ArrayIndexOutOfBoundsException 或IndexOutOfBoundsException 时,则回传null。

● 假若 value-a.get(value-b)或Array.get(value-a, value-b)产生其他的异常时,则会产生

error。

● 最后都没有任何异常产生时,回传value-a.get(value-b)或Array.get(value-a, value-b)。

(5) 当expr-a 的值为JavaBean 对象时:

● 将 value-b 的值强制转型为String。

● 假若 getter 产生异常时,则会产生error。若没有异常产生时,则回传getter 的结果。

6-2-2 EL 变量

EL 存取变量数据的方法很简单,例如:${username}。它的意思是取出某一范围中名称为username

的变量。因为我们并没有指定哪一个范围的username,所以它的默认值会先从Page 范围找,假如

找不到,再依序到Request、Session、Application范围。假如途中找到username,就直接回传,

不再继续找下去,但是假如全部的范围都没有找到时,就回传null(见表6-1):

表 6-1

属性范围在 EL 中的名称

Page PageScope

Request RequestScope

Session SessionScope

Application ApplicationScope

自动搜索顺序

我们也可以指定要取出哪一个范围的变量(见表6-2):

表 6-2

范例 说 明

${pageScope.username} 取出Page 范围的username 变量

${requestScope.username} 取出Request 范围的username 变量

${sessionScope.username} 取出Session 范围的username 变量

${applicationScope.username} 取出Application 范围的username 变量

其中,pageScope、requestScope、sessionScope和applicationScope都是EL 的隐含对象,由

它们的名称可以很容易猜出它们所代表的意思,例如:${sessionScope.username}是取出Session

范围的username 变量。这种写法是不是比之前JSP 的写法:

String username = (String) session.getAttribute("username");

容易、简洁许多。有关EL 隐含对象在6-3 节中有更详细的介绍。

6-2-3 自动转变类型

EL 除了提供方便存取变量的语法之外,它另外一个方便的功能就是:自动转变类型,我们来看

下面这个范例:

${param.count + 20}

假若窗体传来count的值为10时,那么上面的结果为30。之前没接触过JSP 的读者可能会认为

上面的例子是理所当然的,但是在JSP 1.2 之中不能这样做,原因是从窗体所传来的值,它们的类

型一律是String,所以当你接收之后,必须再将它转为其他类型,如:int、float 等等,然后才能

执行一些数学运算,下面是之前的做法:

String str_count = request.getParameter("count");

int count = Integer.parseInt(str_count);

count = count + 20;

接下来再详细说明EL类型转换的规则:

(1) 将A 转为String 类型

● 假若 A 为String 时:回传A

● 否则,当A 为null 时:回传 ""

● 否则,当A.toString( )产生异常时:错误!

● 否则,回传 A.toString( )

(2) 将A 转为Number 类型的N

● 假若 A 为null 或 "" 时:回传0

● 假若 A 为Character 时:将A 转为 new Short((short)a.charValue( ))

● 假若 A 为Boolean 时:错误!

● 假若 A 为Number 类型和N 一样时:回传A

● 假若 A 为Number 时:

·假若N 是BigInteger 时:

·假若A 为BigDecimal 时:回传 A.toBigInteger( )

·否则,回传 BigInteger.valueOf(A.longValue( ))

·假若N 是BigDecimal 时:

·假若A 为BigInteger 时:回传 A.toBigDecimal( )

·否则,回传 BigDecimal.valueOf(A.doubleValue( ))

·假若N 为Byte 时:回传 new Byte(A.byteValue( ))

·假若N 为Short 时:回传 new Short(A.shortValue( ))

·假若N 为Integer 时:回传 new Integer(A.intValue( ))

·假若N 为Long 时:回传 new Long(A.longValue( ))

·假若N 为Float 时:回传 new Float(A.floatValue( ))

·假若N 为Double 时:回传 new Double(A.doubleValue( ))

·否则,错误!

● 假若 A 为String 时:

·假若N 是BigDecimal 时:

·假若 new BigDecimal(A)产生异常时:错误!

·否则,回传 new BigDecimal(A)

·假若N 是BigInteger 时:

·假若 new BigInteger(A)产生异常时:错误!

·否则,回传 new BigInteger(A)

·假若N.valueOf(A)产生异常时:错误!

·否则,回传 N.valueOf(A)

● 否则,错误!

(3) 将A 转为Character 类型

● 假若 A 为null 或 "" 时:回传 (char)0

● 假若 A 为Character 时:回传 A

● 假若 A 为Boolean 时:错误!

● 假若 A 为Number 时:转换为Short 后,然后回传Character

● 假若 A 为String 时:回传 A.charAt(0)

● 否则,错误!

(4) 将A 转为Boolean 类型

● 假若 A 为null 或 "" 时:回传 false

● 否则,假若A 为Boolean 时:回传 A

● 否则,假若A为String,且Boolean.valueOf(A)没有产生异常时:回传Boolean.valueOf(A)

● 否则,错误!

6-2-4 EL 保留字

EL 的保留字如表6-3:

表 6-3

And eq gt true

Or ne le false

No lt ge null

instanceof empty div mod

所谓保留字的意思是指变量在命名时,应该避开上述的名字,以免程序编译时发生错误。

6-3 EL 隐含对象

笔者在“第五章:隐含对象(Implicit Object)”中,曾经介绍过9 个JSP 隐含对象,而EL本

身也有自己的隐含对象。EL 隐含对象总共有11 个(见表6-4):

表 6-4

隐含对象类 型 说 明

PageContext javax.servlet.ServletContext 表示此JSP 的PageContext

PageScope java.util.Map 取得Page范围的属性名称所对应的值

RequestScope java.util.Map

取得Request 范围的属性名称所对应

的值

sessionScope java.util.Map

取得Session 范围的属性名称所对应

的值

applicationScope java.util.Map

取得Application 范围的属性名称所

对应的值

param java.util.Map

如同

ServletRequest.getParameter(String

name)。回传String 类型的值

续表

隐含对象类 型 说 明

paramValues java.util.Map 如同

隐含对象类 型 说 明

ServletRequest.getParameterValues(String

name)。回传String []类型的值

header java.util.Map

如同ServletRequest.getHeader(String

name)。回传String 类型的值

headerValues java.util.Map

如同ServletRequest.getHeaders(String

name)。回传String []类型的值

cookie java.util.Map 如同HttpServletRequest.getCookies( )

initParam java.util.Map

如同

ServletContext.getInitParameter(String

name)。回传String 类型的值

这 11 个隐含对象(Implicit Object),笔者将它分成三类:

1. 与范围有关的隐含对象

applicationScope

sessionScope

requestScope

pageScope

2. 与输入有关的隐含对象

param

paramValues

3. 其他隐含对象

cookie

header

headerValues

initParam

pageContext

接下来笔者会依照上面的分类顺序,为读者介绍这些隐含对象。

6-3-1 属性(Attribute)与范围(Scope)

与范围有关的EL 隐含对象包含以下四个:pageScope、requestScope、sessionScope 和

applicationScope,它们基本上就和JSP的pageContext、request、session和application一样,

所以笔者在这里只稍略说明。不过必须注意的是,这四个隐含对象只能用来取得范围属性值,即JSP

中的getAttribute(String name),却不能取得其他相关信息,例如:JSP中的request对象除可以存

取属性之外,还可以取得用户的请求参数或表头信息等等。但是在EL中,它就只能单纯用来取得对应

范围的属性值,例如:我们要在session 中储存一个属性,它的名称为username,在JSP 中使用

session.getAttribute("username") 来取得username 的值, 但是在EL 中, 则是使用

${sessionScope.username}来取得其值的。接下来分别对这四个隐含对象做简短的说明:

● pageScope

范围和JSP 的Page 相同,也就是单单一页JSP Page 的范围(Scope)。

● requestScope

范围和JSP 的Request 相同,requestScope 的范围是指从一个JSP 网页请求到另一个JSP 网页

请求之间,随后此属性就会失效。

● sessionScope

范围和JSP Scope 中的session 相同,它的属性范围就是用户持续在服务器连接的时间。

● applicationScope

范围和JSP Scope 中的application 相同,它的属性范围是从服务器一开始执行服务,到服务

器关闭为止。

6-3-2 与输入有关的隐含对象

与输入有关的隐含对象有两个:param和paramValues,它们是EL中比较特别的隐含对象。一般

而言,我们在取得用户的请求参数时,可以利用下列方法:

request.getParameter(String name)

request.getParameterValues(String name)

在 EL中则可以使用param和paramValues两者来取得数据。

${param.name}

${paramValues.name}

这里param 的功能和request.getParameter(String name) 相同, 而paramValues 和

request.getParameterValues(String name)相同。如果用户填了一个表格,表格名称为username,则我们

就可以使用${param.username}来取得用户填入的值。

为了让读者更加了解param 和paramValues 隐含对象的使用,再来看下面这个范例。此范例共

有两个文件,分别为给用户输入值用的Param.html 和显示出用户所传之值的Param.jsp。

Param.html

<html>

<head>

<title>CH6 - Param.html</title>

</head>

<body>

<h2>EL 隐含对象 param、paramValues</h2>

<form method = "post" action = "Param.jsp">

<p>姓名:<input type="text" name="username" size="15" /></p>

<p>密码:<input type="password" name="password" size="15" /></p>

<p>性别:<input type="radio" name="sex" value="Male" checked/> 男

<input type="radio" name="sex" value="Female" /> 女</p>

<p>年龄:

<select name="old">

<option value="10">10 - 20</option>

<option value="20" selected>20 - 30</option>

<option value="30">30 - 40</option>

<option value="40">40 - 50</option>

</select>

</p>

<p>兴趣:

<input type="checkbox" name="habit" value="Reading"/>看书

<input type="checkbox" name="habit" value="Game"/>玩游戏

<input type="checkbox" name="habit" value="Travel"/>旅游

<input type="checkbox" name="habit" value="Music"/>听音乐

<input type="checkbox" name="habit" value="Tv"/>看电视

</p>

<p>

<input type="submit" value="传送"/>

<input type="reset" value="清除"/>

</p>

</form>

</body>

</html>

Param.html 的执行结果如图6-1 所示。当我们把窗体填好后按下传送钮,它将会把信息传送到

Param.jsp 做处理。

图 6-1 Param.html 的执行结果,并填入信息

接下来,Param.jsp 接收由Param.html 传来的信息,并且将它显示出来:

Param.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<html>

<head>

<title>CH6 - Param.jsp</title>

</head>

<body>

<h2>EL 隐含对象 param、paramValues</h2>

<fmt:requestEncoding value="GB2312" />

姓名: ${param.username}</br>

密码: ${param.password}</br>

性别: ${param.sex}</br>

年龄: ${param.old}</br>

兴趣: ${paramValues.habit[0]}

${paramValues.habit[1]}

</body>

</html>

由Param.html 窗体传过来的值,我们必须指定编码方式,才能够确保Param.jsp能够顺利接收中

文,传统的做法为:

<%

request.setCharacterEncoding("GB2312");

%>

假若是使用JSTL写法时,必须使用I18N 格式处理的标签库,如下:

<fmt:requestEncoding value="GB2312" />

Param.jsp主要使用EL的隐含对象param来接收数据。但是必须注意:假若要取得多重选择的复

选框的值时,必须使用paramValues,例如:使用paramValues 来取得“兴趣”的值,不过这里笔者

最多只显示两笔“兴趣”的值:

${param.username}

………

${paramValues.habit[0]}

${paramValues.habit[1]}

有关JSTL的使用,第七章有更加详细的说明。图6-2是Param.jsp的执行结果:

图 6-2 Param.jsp 的执行结果

6-3-3 其他隐含对象

介绍完上面六个隐含对象后,接下来将介绍最后五个隐含对象。

● cookie

所谓的cookie是一个小小的文本文件,它是以key、value的方式将Session Tracking的内容记录

在这个文本文件内,这个文本文件通常存在于浏览器的暂存区内。JSTL并没有提供设定cookie的动作,

因为这个动作通常都是后端开发者必须去做的事情,而不是交给前端的开发者。假若我们在cookie 中

设定一个名称为userCountry的值,那么可以使用${cookie.userCountry}来取得它。

● header 和headerValues

header 储存用户浏览器和服务端用来沟通的数据,当用户要求服务端的网页时,会送出一个记

载要求信息的标头文件,例如:用户浏览器的版本、用户计算机所设定的区域等其他相关数据。假

若要取得用户浏览器的版本,即${header["User-Agent"]}。另外在鲜少机会下,有可能同一标头名

称拥有不同的值,此时必须改为使用headerValues 来取得这些值。

注意

因为User-Agent 中包含“-”这个特殊字符,所以必须使用“[]”,而不能写成

$(header.User-Agent)。

● initParam

就像其他属性一样,我们可以自行设定web 站台的环境参数(Context),当我们想取得这些参数

时,可以使用initParam 隐含对象去取得它,例如:当我们在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">

<context-param>

<param-name>userid</param-name>

<param-value>mike</param-value>

</context-param>

</web-app>

那么我们就可以直接使用 ${initParam.userid}来取得名称为userid,其值为mike 的参数。下

面是之前的做法:

String userid = (String)application.getInitParameter("userid");

● pageContext

我们可以使用 ${pageContext}来取得其他有关用户要求或页面的详细信息。表6-5 列出了几个

比较常用的部分。

表 6-5

Expression 说明

${pageContext.request.queryString} 取得请求的参数字符串

${pageContext.request.requestURL} 取得请求的URL,但不包括请求之参数字符串

${pageContext.request.contextPath} 服务的web application 的名称

${pageContext.request.method} 取得HTTP 的方法(GET、POST)

${pageContext.request.protocol} 取得使用的协议(HTTP/1.1、HTTP/1.0)

${pageContext.request.remoteUser} 取得用户名称

${pageContext.request.remoteAddr } 取得用户的IP 地址

${pageContext.session.new}

判断session 是否为新的,所谓新的session,

表示刚由server 产生而client 尚未使用

${pageContext.session.id} 取得session 的ID

${pageContext.servletContext.serverInfo}取得主机端的服务信息

我们来看下面这个范例:pageContext.jsp,相信对读者来说能更加了解pageContext的用法。

pageContext.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH6 - pageContext.jsp</title>

</head>

<body>

<h2>EL 隐含对象 pageContext</h2>

\${pageContext.request.queryString}:${pageContext.request.queryString}</br>

\${pageContext.request.requestURL}:${pageContext.request.requestURL}</br>

\${pageContext.request.contextPath}:${pageContext.request.contextPath}</br>

\${pageContext.request.method}:${pageContext.request.method}</br>

\${pageContext.request.protocol}:${pageContext.request.protocol}</br>

\${pageContext.request.remoteUser}:${pageContext.request.remoteUser}</br>

\${pageContext.request.remoteAddr }:${pageContext.request.remoteAddr}</br>

\${pageContext.session.new}:${pageContext.session.new}</br>

\${pageContext.session.id}:${pageContext.session.id}</br>

</body>

</html>

pageContext.jsp 的执行结果如图6-3,执行时必须在pageContext.jsp 之后加上?test=1234,即

PageContext.jsp?test=1234,这样${pageContext.request.queryString}才会显示test=1234。

图 6-3 pageContext.jsp 的执行结果

注意

因为 ${} 在JSP 2.0 中是特殊字符,JSP容器会自动将它当做EL来执行,因此,假若要显

示 ${}时,必须在 $ 前加上 \ ,如:\${ XXXXX }

6-4 EL 算术运算符

EL 算术运算符主要有以下五个(见表6-6):

表 6-6

算术运算符说 明 范 例 结 果

+ 加${ 17 + 5 } 22

- 减 ${ 17 - 5 } 12

* 乘${ 17 * 5 } 85

/ 或 div 除${ 17 / 5 } 或 ${ 17 div 5 } 3

% 或 mod 余数${ 17 % 5 } 或 ${ 17 mod 5 } 2

接下来,我们依照下列几种情况,详细说明EL 算术运算符的规则:

(1) A {+ ,- , *} B

● 假若 A 和B 为null:回传 (Long)0

● 假若 A 或B 为BigDecimal 时,将另一个也转为BigDecimal,则:

·假若运算符为 + 时:回传 A.add(B)

·假若运算符为- 时:回传 A.subtract(B)

·假若运算符为 * 时:回传 A.multiply(B)

● 假若 A 或B 为Float、Double 或包含 e / E 的字符串时:

·假若A 或B 为BigInteger 时,将另一个转为BigDecimal,然后依照运算符执行

运算

·否则,将两者皆转为Double,然后依照运算符执行运算

● 假若 A 或B 为BigInteger 时,将另一个也转为BigInteger,则:

·假若运算符为 + 时:回传 A.add(B)

·假若运算符为- 时:回传 A.subtract(B)

·假若运算符为 * 时:回传 A.multiply(B)

● 否则,__________将A 和B 皆转为Long,然后依照运算符执行运算

● 假若运算结果产生异常时,则错误!

(2) A {/ , div} B

● 假若 A 和B 为null:回传 (Long)0

● 假若 A 或B 为BigDecimal 或BigInteger 时,皆转为BigDecimal,然后回传 A.divide(B,

BigDecimal.ROUND_HALF_UP)

● 否则,将A 和B 皆转为Double,然后依照运算符执行运算

● 假若运算结果产生异常时,则错误!

(3) A {% , mod} B

● 假若 A 和B 为null:回传 (Long)0

● 假若A 或B为BigDecimal、Float、Double或包含 e / E 的字符串时,皆转为Double,然

后依照运算符执行运算

● 假若 A 或B 为BigInteger 时,将另一个转为BigInteger,则回传 A.remainder(B)

● 否则,将A 和B 皆转为Long,然后依照运算符执行运算

● 假若运算结果产生异常时,则错误!

(4) -A

● 假若 A 为null:回传 (Long)0

● 假若 A 为BigDecimal 或BigInteger 时,回传 A.negate( )

● 假若 A 为String 时:

·假若A 包含 e / E 时,将转为Double,然后依照运算符执行运算

·否则,转为Long,然后依照运算符执行运算

·假若运算结果产生异常时,则错误!

● 假若 A 为Byte、Short、Integer、Long、Float 或Double

·直接依原本类型执行运算

·假若运算结果产生异常时,则错误!

● 否则,错误!

Tomcat 上的jsp-examples 中,有一个EL算术运算符的范例 basic-arithmetic.jsp。它的程序

很简单,所以不在这里多做说明,它的执行结果如图6-4 所示。

图 6-4 basic-arithmetic.jsp 的执行结果

6-5 EL 关系运算符

EL 关系运算符有以下六个运算符(见表6-7):

表 6-7

关系运算符说 明 范 例 结 果

= = 或 eq 等于${ 5 = = 5 } 或 ${ 5 eq 5 } true

!= 或 ne 不等于${ 5 != 5 } 或 ${ 5 ne 5 } false

< 或 lt 小于${ 3 < 5 }或 ${ 3 lt 5 } true

> 或 gt 大于${ 3 > 5 }或 ${ 3 gt 5 } false

<= 或 le 小于等于${ 3 <= 5 }或 ${ 3 le 5 } true

>= 或 ge 大于等于${ 3 >= 5 }或 ${ 3 ge 5 } false

注意

在使用EL 关系运算符时,不能够写成:

${param.password1} = = ${param.password2}

或者

${ ${param.password1 } = = ${ param.password2 } }

而应写成

${ param.password1 = = param.password2 }

接下来,我们依照下列几种情况,详细说明EL 关系运算符的规则:

(1) A {<, >, <=, >=, lt, gt, le, ge} B

● 假若 A= = B,运算符为<=, le, >=, ge 时,回传true,否则回传false

● 假若 A 为null 或B 为null 时,回传false

● 假若A或B为BigDecimal时,将另一个转为BigDecimal,然后回传A.compareTo(B)的值

● 假若 A 或B 为Float、__________Double 时,皆转为Double 类型,然后依其运算符运算

● 假若A或B为BigInteger时,将另一个转为BigInteger,然后回传A.compareTo(B)的值

● 假若A 或B为Byte、Short、Character、Integer或Long时,皆转为Long 类型,然后依其

运算符运算

● 假若 A 或B 为String 时,将另一个也转为String,然后做词汇上的比较

● 假若 A 为Comparable 时,则:

·假若A.compareTo(B)产生异常时,则错误!

● 否则,采用 A.compareTo(B) 的比较结果

● 假若 B 为Comparable 时,则:

· 假若 B.compareTo(A)产生异常时,则错误!

● 否则,采用 A.compareTo(B) 的比较结果

● 否则,错误!

(2) A {= =, !=, eq, ne} B

● 假若 A= = B,依其运算符运算

● 假若 A 为null 或B 为null 时:= = /eq 则回传false,!= / ne 则回传true

● 假若 A 或B 为BigDecimal 时,将另一个转为BigDecimal,则:

· 假若运算符为 = = / eq,则回传A.equals(B)

· 假若运算符为 != / ne,则回传 !A.equals(B)

● 假若 A 或B 为Float、Double 时,皆转为Double 类型,然后依其运算符运算

● 假若 A 或B 为BigInteger 时,将另一个转为BigInteger,则:

·假若运算符为 = = / eq,则回传A.equals(B)

·假若运算符为 != / ne,则回传 !A.equals(B)

● 假若A 或B为Byte、Short、Character、Integer 或Long时,皆转为Long 类型,然后依其

运算符运算

● 假若 A 或B 为Boolean 时,将另一个也转为Boolean,然后依其运算符运算

● 假若 A 或B 为String 时,将另一个也转为String,然后做词汇上的比较

● 否则,假若A.equals(B)产生异常时,则错误!

● 否则,然后依其运算符运算,回传A.equals(B)

Tomcat 上的jsp-examples 中,有一个EL 关系运算符的范例basic-comparisons.jsp。它的程

序很简单,所以不在这里多做说明,大家直接看它的执行结果(如图6-5 所示):

图 6-5 basic-comparisons.jsp 的执行结果

6-6 EL 逻辑运算符

EL 逻辑运算符只有三个(见表6-8):

表 6-8

逻辑运算符说 明 范 例 结 果

&& 或 and 交集${ A && B } 或 ${ A and B } true / false

|| 或 or 并集${ A || B } 或 ${ A or B } true / false

! 或 not 非${ !A } 或 ${ not A } true / false

下面举几个例子:

${ param.month = = 7 and param.day = = 14 }

${ param.month = = 7 || param.day = = 14 }

${ not param.choice }

EL 逻辑运算符的规则很简单:

(1) A {&&, and, || 或 or } B

·将A 和B 转为Boolean,然后依其运算符运算

(2) {!, not}A

·将A 转为Boolean,然后依其运算符运算

6-7 EL 其他运算符

EL 除了上述三大类的运算符之外___________,还有下列几个重要的运算符:

(1) Empty 运算符

(2) 条件运算符

(3) ( ) 括号运算符

6-7-1 Empty 运算符

Empty 运算符主要用来判断值是否为null 或空的,例如:

${ empty param.name }

接下来说明Empty 运算符的规则:

(1) {empty} A

● 假若 A 为null 时,回传true

● 否则,假若A 为空String 时,回传true

● 否则,假若A 为空Array 时,回传true

● 否则,假若A 为空Map 时,回传true

● 否则,假若A 为空Collection 时,回传true

● 否则,回传false

6-7-2 条件运算符

所谓条件运算符如下:

${ A ? B : C}

意思是说,当A 为true 时,执行B;而A 为false 时,则执行C。

6-7-3 括号运算符

括号运算符主要用来改变执行优先权,例如:${ A * (B+C) }

至于运算符的优先权,如下所示(由高至低,由左至右):

· []、.

· ( )

· - (负)、not、!、empty

· *、/、div、%、mod

· +、- (减)

· <、>、<=、>=、lt、gt、le、ge

· = =、!=、eq、ne

· &&、and

· ||、or

· ${ A ? B : C}

最后笔者写一个ELOperator.jsp 范例,将所有运算符实际操作一遍。

ELOperator.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>

<head>

<title>CH6 – ELOperator.jsp</title>

</head>

<body>

<h2>EL 的运算符</h2>

<c:set value="mike" var="username" scope="request" />

<table border="1" width="50%" align="left">

<TR>

<TR>

<TH>运算式</TH>

<TH>结果</TH>

</TR>

<TR><TD>14 + 3</TD><TD>${14 + 3}</TD></TR>

<TR><TD>14 - 3</TD><TD>${14 - 3}</TD></TR>

<TR><TD>14 * 3</TD><TD>${14 * 3}</TD></TR>

<TR><TD>14 / 3</TD><TD>${14 / 3}</TD></TR>

<TR><TD>14 % 3</TD><TD>${14 % 3}</TD></TR>

<TR><TD>14 == 3</TD><TD>${14 == 3}</TD></TR>

<TR><TD>14 != 3</TD><TD>${14 != 3}</TD></TR>

<TR><TD>14 < 3</TD><TD>${14 < 3}</TD></TR>

<TR><TD>14 > 3</TD><TD>${14 > 3}</TD></TR>

<TR><TD>14 <= 3</TD><TD>${14 <= 3}</TD></TR>

<TR><TD>14 >= 3</TD><TD>${14 >= 3}</TD></TR>

<TR><TD>true && false</TD><TD>${true && false}</TD></TR>

<TR><TD>true || false</TD><TD>${true || false}</TD></TR>

<TR><TD>! false</TD><TD>${! false}</TD></TR>

<TR><TD>empty username</TD><TD>${empty username}</TD></TR>

<TR><TD>empty password</TD><TD>${empty password}</TD></TR>

</table>

</body>

</html>

EL 的数学运算符、相等运算符、关系运算符和逻辑运算符就跟其他程序语言一样,并没有特别

的地方。但是它的empty 运算符就比较特别,为了测试它,笔者写了这样一行程序代码:

<c:set value="mike" var="username" scope="request" />

这样Request 属性范围里就存在一个名称为username、值为mike 的属性。执行此程序时,读者

将会发现${empty username}为false; ${empty password}为true,其代表的意义就是:它可以在

四种属性范围中找到username这个属性,但是找不到password这个属性。ELOperator.jsp 的执行

结果如图6-6:

图 6-6 ELOperator.jsp 的执行结果

6-8 EL Functions

前面几节主要介绍EL 语法的使用和规则,本节笔者将介绍如何自定义EL 的函数(functions)。

EL 函数的语法如下:

ns:function( arg1, arg2, arg3 …. argN)

其中ns 为前置名称(prefix),它必须和taglib 指令的前置名称一样。如下范例:

<% @ taglib prefix="my"

uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib" %>

…….

${my:function(param.name)}

前置名称都为my,至于function 为EL 函数的名称,而arg1、arg2 等等,都是function 的传

入值。在Tomcat 5.0.16中有一个简单的EL 函数范例,名称为functions.jsp,笔者接下来将依此

范例来说明如何自定义EL 函数。

6-8-1 Tomcat EL 函数范例

Tomcat 提供的EL 函数范例中,自定义两个EL 函数:reverse 和countVowels,其中:

reverse 函数:将传入的字符串以反向顺序输出。

countVowels 函数:计算传入的字符串中,和aeiouAEIOU 吻合的字符个数。

图 6-7 是functions.jsp 程序的执行结果:

图 6-7 functions.jsp 的执行结果

输入JavaWorld.com.tw 字符串至reverse 函数后,回传 wt.moc.dlroWavaJ 的结果;若传入

countVowels 函数后,因为有两个a 和o,总共四个字符吻合,所以回传4。

Tomcat 的EL 函数范例,主要分为四个部分(见表6-9):

表 6-9

web.xml 设定taglib 的TLD 文件位置

functions.jsp 使用EL 函数的范例程序

jsp2-example-taglib.tld EL 函数、标签库的设定文件

jsp2.examples.el.Functions.java EL 函数主要程序逻辑处理部分

这四个部分环环相扣,都互有关系,笔者依functions.jsp为中心,然后再慢慢说明其他部分。

首先我们直接来看functions.jsp 程序:

6-8-2 functions.jsp

functions.jsp

<%@ taglib prefix="my"

uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>

<html>

<head>

<title>JSP 2.0 Expression Language - Functions</title>

</head>

<body>

<h1>JSP 2.0 Expression Language - Functions</h1>

…. 略

<blockquote>

<u><b>Change Parameter</b></u>

<form action="functions.jsp" method="GET">

foo = <input type="text" name="foo" value="${param['foo']}">

<input type="submit">

</form>

<br>

<code>

<table border="1">

<thead>

<td><b>EL Expression</b></td>

<td><b>Result</b></td>

</thead>

<tr>

<td>\${param["foo"]}</td>

<td>${param["foo"]}&nbsp;</td>

</tr>

<tr>

<td>\${my:reverse(param["foo"])}</td>

<td>${my:reverse(param["foo"])}&nbsp;</td>

</tr>

<tr>

<td>\${my:reverse(my:reverse(param["foo"]))}</td>

<td>${my:reverse(my:reverse(param["foo"]))}&nbsp;</td>

</tr>

<tr>

<td>\${my:countVowels(param["foo"])}</td>

<td>${my:countVowels(param["foo"])}&nbsp;</td>

</tr>

</table>

</code>

</blockquote>

</body>

</html>

functions.jsp程序中,一开始定义taglib,它的前置名称为my;uri为http://jakarta.apache.org/

tomcat/jsp2-example-taglib,如下所示:

<%@ taglib prefix="my"

uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib"%>

当Container执行这段程序时,它会根据uri的值,到web.xml中找相对应的TLD (Tag Library

Descriptor)文件。至于web.xml 如何设定两者之间的对应关系,我们在6-8-3 小节再说明。

functions.jsp 中包含一个窗体(form),当用户在文本[玉玉1]输入框(text input)中输入字符

串,按下按钮时,底下会显示字符串经过EL 函数处理后的结果。functions.jsp 程序最重要的部分

是调用EL 函数:

${my:reverse(param["foo"])}

上述的意思是接收foo参数,然后传入reverse 函数。调用EL函数的方式很简单,只要前置名

称:其中EL 函数名称是被定义在TLD 文件中,这会在6-8-4 小节详细说明。至于reverse 函数的逻

辑运算,则是被定义在jsp2.examples.el.Functions.java程序中,这部分会在6-8-5 小节中说明。

注意

TLD 文件主要为标签的设定文件,其中包含标签的名称、参数等等。在JSP 2.0 之后,相关

EL 函数的设定,也可以在TLD 文件中定义。

6-8-3 web.xml

web.xml是每个web站台最主要的设定文件,在这个设定文件中,可以设定许多东西,如:Servlet、

Resource、Filter 等等。不过现在关心的是如何在web.xml 中设定taglib的uri 是对应到哪个TLD

文件。笔者从范例的web.xml 中节录出设定的片段程序如下:

web.xml

<jsp-config>

<taglib>

<taglib-uri>

http://jakarta.apache.org/tomcat/jsp2-example-taglib

</taglib-uri>

<taglib-location>

/WEB-INF/jsp2/jsp2-example-taglib.tld

</taglib-location>

</taglib>

</jsp-config>

在web.xml 中,<taglib>用来设定标签的TLD 文件位置。<taglib-uri>用来指定taglib 的uri

位置,用户可以自行给定一个uri,例如:

<taglib-uri>http://www.javaworld.com.tw/jute</taglib-uri>

<taglib-uri>tw.com.javaworld</taglib-uri>

<taglib-location>用来指定TLD 文件的位置。依照范例,它是指定在WEB-INF/jsp2/目录下的

jsp2-example-taglib.tld。

因 此 ,笔者所节录下来的web.xml , 它所代表的意思是: taglib 的uri 为

http://jakarta.apache.org/tomcat/jsp2-example-taglib,它的TLD 文件是在WEB-INF/jsp2/目

录下的jsp2-example-taglib.tld。

6-8-4 jsp2-example-taglib.tld

在jsp2-example-taglib.tld 中定义许多标签,其中笔者节录一段定义EL 函数:

jsp2-example-taglib.tld

<function>

<description>Reverses the characters in the given String</description>

<name>reverse</name>

<function-class>jsp2.examples.el.Functions</function-class>

<function-signature>

java.lang.String reverse( java.lang.String )

</function-signature>

</function>

<function>

<description>Counts the number of vowels (a,e,i,o,u) in the given

String</description>

<name>countVowels</name>

<function-class>jsp2.examples.el.Functions</function-class>

<function-signature>

java.lang.String numVowels( java.lang.String )

</function-signature>

</function>

上述定义两个EL 函数,用<name>来设定EL 函数名称,它们分别为reverse和countVowels;用

<function-class> 设定EL 函数的Java 类, 本范例的EL 函数都是定义在

jsp2.examples.el.Functions;最后用<function-signature>来设定EL 函数的传入值和回传值,例

如:

<function-signature>java.lang.String

reverse( java.lang.String )</function-signature>

表示reverse 函数有一String 类型的传入值,然后回传String 类型的值。最后我们再来看

reverse 和countVowels 的程序。

6-8-5 Functions.java

Functions.java 主要定义三个公开静态的方法,分别为:reverse、numVowels 和caps(见表

6-10)。下面是Functions.java 完整的程序代码:

Functions.java

package jsp2.examples.el;

import java.util.*;

/**

* Defines the functions for the jsp2 example tag library.

* <p>Each function is defined as a static method.</p>

*/

public class Functions {

public static String reverse( String text ) {

return new StringBuffer( text ).reverse().toString();

}

public static int numVowels( String text ) {

String vowels = "aeiouAEIOU";

int result = 0;

for( int i = 0; i < text.length(); i++ ) {

if( vowels.indexOf( text.charAt( i ) ) != -1 ) {

result++;

}

}

return result;

}

public static String caps( String text ) {

return text.toUpperCase();

}

}

表 6-10

String reverse(String

text)

将text 字符串的顺序反向处理,然后回传反向后的字符串

int numVowels(String text)将 text 字符串比对aeiouAEIOU 等字符,然后回传比对中的次

_______________数

String caps(String text) 将 text 字符串都转为大写,然后回传此字符串

注意

在定义EL 函数时,都必须为公开静态(public static)

JSP错误处理

4-7 错误处理

通常 JSP 在执行时,在两个阶段下会发生错误。

JSP 网页→ servlet

servlet 类处理每一个请求时

在第一阶段时,产生的错误我们称为Translation Time Processing Errors;在第二阶段时,

产生的错误我们称为Client Request Time Processing Errors。接下来我们将针对这两个阶段产

生错误的原因和处理方法,为各位读者做详细的介绍。

4-7-1 Translation Time Processing Errors

Translation Time Processing Errors 产生的主要原因:我们在撰写JSP时的语法有错误,导

致JSP Container 无法将JSP 网页编译成Servlet 类文件( .class),例如:500 Internal Server

Error,500 是指HTTP 的错误状态码,因此是Server Error,如图4-8。

通常产生这种错误时,可能是JSP 的语法有错误,或是JSP Container 在一开始安装、设定时,

有不适当的情形发生。解决的方法就是再一次检查程序是否有写错的,不然也有可能是JSP

Container 的bug。

图 4-8 500 Internal Server Error

4-7-2 Client Request Time Processing Errors

Client Request Time Processing Errors 错误的发生,往往不是语法错误,而可能是逻辑上

的错误,简单地说,你写一个计算除法的程序,当用户输入的分母为零时,程序会发生错误并抛出

异常(Exception),交由异常处理(Exception Handling)机制做适当的处理。

对于这种错误的处理,我们通常会交给errorPage 去处理。下面举个例子,我想各位读者应该

能够很容易了解。

使用errorPage 的范例程序 (一):ErrorPage.jsp

ErrorPage.jsp

<%@ page contentType="text/html;charset=GB2312" errorPage="Error.jsp" %>

<html>

<head>

<title>CH4 - ErrorPage.jsp</title>

</head>

<body>

<h2>errorPage 的范例程序</h2>

<%!

private double toDouble(String value)

{

return(Double.valueOf(value).doubleValue());

}

%>

<%

double num1 = toDouble(request.getParameter("num1"));

double num2 = toDouble(request.getParameter("num2"));

%>

您传入的两个数字为:<%= num1 %> 和 <%= num2 %><br>

两数相加为 <%= (num1+num2) %>

</body>

</html>

ErrorPage.jsp 程序中,我们使用page 指令中的errorPage 属性,告诉JSP Container,如果

在程序中有错误产生时,会自动交给Error.jsp 处理。

<%@ page contentType="text/html;charset=GB2312 " errorPage="Error.jsp" %>

我们声明一个函数叫toDouble,主要把我们接收进来的字符串转成Double 的类型,才能做加

法运算。

<%!

private double toDouble(String value)

{

return(Double.valueOf(value).doubleValue());

}

%>

接收num1 和num2 两个字符串,并且将它们转为double 的类型,并各自命名为num1 和num2。

<%

double num1 = toDouble(request.getParameter("num1"));

double num2 = toDouble(request.getParameter("num2"));

%>

Error.jsp

<%@ page contentType="text/html;charset=GB2312" isErrorPage="true" %>

<%@ page import="java.io.PrintWriter" %>

<html>

<head>

<title>CH4 - Error.jsp</title>

</head>

<body>

<h2>errorPage 的范例程序</h2>

<p>ErrorPage.jsp 错误产生:<I><%= exception %></I></p><br>

<pre>

问题如下:<% exception.printStackTrace(new PrintWriter(out)); %>

</pre>

</body>

</html>

Error.jsp 主要处理ErrorPage.jsp 所产生的错误,所以在ErrorPage.jsp 中page 指令的属性

errorPage设为Error.jsp,因此,若ErrorPage.jsp有错误发生时,会自动转到Error.jsp来处理。

Error.jsp 必须设定page 指令的属性isErrorPage为true,因为Error.jsp是专门用来处理错

误的网页。

<%@ page contentType="text/html;charset=GB2312" isErrorPage="true" %>

下面这几行代码用来显示出错误发生的原因:

<p>ErrorPage.jsp 错误产生:<I><%= exception %></I></p><br>

<PRE>

问题如下:<% exception.printStackTrace(new PrintWriter(out)); %>

</PRE>

由于在这个程序中并没有做一个窗体来输入两个数字,所以必须手动在URL后输入num1和num2

的值。如图4-9 所示:

图 4-9 ErrorPage.jsp 的执行结果

上一个范例的参数设定为:

http://localhost:8080/JSPBook/CH4/ErrorPage.jsp?num1=100&num2=245

此时num1 的值为100,num2 的值为245,所以相加的结果就为345。但是,如果num1 或num2

的参数不是数字的话,例如:

http://localhost:8080/JSPBook/CH4/ErrorPage.jsp?num1=javaworld&num2=245

那么就会产生错误结果如图4-10。

当 ErrorPage.jsp 产生错误时,就会交由Error.jsp 去处理,所以您看到的结果,其实是执行

Error.jsp 后的结果。

图 4-10 ErrorPage.jsp 执行时产生错误

5-1 属性( Attribute )与范围( Scope )

有些JSP 程序员会将request、session、application 和pageContext 归为一类,原因在于:

它们皆能借助setAttribute( )和getAttribute( )来设定和取得其属性(Attribute),通过这两种

方法来做到数据分享

我们先来看下面这段小程序:

Page1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Page1.jsp</title>

</head>

<body>

</br>

<%

application.setAttribute("Name","mike");

application.setAttribute("Password","browser");

%>

<jsp:forward page="Page2.jsp"/>

</body>

</html>

在这个程序中,笔者设定两个属性:Name、Password,其值为:mike、browser。然后再转交(forward)

到Page2.jsp。我只要在Page2.jsp 当中加入application.getAttribute( ),就能取得在Page1.jsp

设定的数据。

Page2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Page2.jsp</title>

</head>

<body>

<%

String Name = (String) application.getAttribute("Name");

String Password = (String) application.getAttribute("Password");

out.println("Name = "+Name);

out.println("Password = "+ Password);

%>

</body>

</html>

Page1.jsp 的执行结果如图5-1 所示。

图 5-1 Page1.jsp 的执行结果

从图5-1 可以看出,此时Name的值就会等于mike,Password 的值就等于browser。看完这个小

范例之后,读者有没有发现网页之间要传递数据时,除了可以使用窗体、隐藏字段来完成之外,JSP

技术还提供给开发人员一项传递数据的机制,那就是利用setAttribute( )和getAttribute( )方法,

如同Page1.jsp 和Page2.jsp 的做法。不过它还是有些限制的,这就留到下一节来说明。

在上面 Page1.jsp 和Page2.jsp 的程序当中,是将数据存入到application 对象之中。除了

application 之外,还有request、pageContext 和session,也都可以设定和取得属性值,那它们

之间有什么分别吗?

它们之间最大的差别在于范围(Scope)不一样,这个概念有点像C、C++中的全局变量和局部变量

的概念。接下来就介绍JSP 的范围。

5-1-1 JSP ScopePage

JSP 有四种范围,分别为Page、Request、Session、Application。所谓的Page,指的是单单一

页JSP Page 的范围。若要将数据存入Page 范围时,可以用pageContext 对象的setAttribute( )

方法;若要取得Page范围的数据时,可以使用pageContext对象的getAttribute( )方法。我们将

之前的范例做小幅度的修改,将application 改为pageContext。

PageScope1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - PageScope1.jsp</title>

</head>

<body>

<h2>Page 范围 - pageContext</h2>

<%

pageContext.setAttribute("Name","mike");

pageContext.setAttribute("Password","browser");

%>

<jsp:forward page="PageScope2.jsp"/>

</body>

</html>

PageScope2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - PageScope2.jsp</title>

</head>

<body>

<h2>Page 范围 - pageContext</h2>

</br>

<%

String Name = (String)pageContext.getAttribute("Name");

String Password = (String)pageContext.getAttribute("Password");

out.println("Name = "+Name);

out.println("Password = "+ Password);

%>

</body>

</html>

执行结果如图5-2 所示。

图 5-2 PageScope1.jsp 的执行结果

这个范例程序和之前有点类似,只是之前的程序是application,现在改为pageContext,但是结

果却大不相同,PageScope2.jsp 根本无法取得PageScope1.jsp 设定的Name 和Password 值,因为在

PageScope1.jsp 当中,是把Name 和Password 的属性范围设为Page,所以Name 和Password 的值只能

在PageScope1.jsp当中取得。若修改PageScope1.jsp的程序,重新命名为PageScope3.jsp,如下:

PageScope3.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - PageScope3.jsp</title>

</head>

<body>

<h2>Page 范围 - pageContext</h2>

</br>

<%

pageContext.setAttribute("Name","mike");

pageContext.setAttribute("Password","browser");

String Name = (String)pageContext.getAttribute("Name");

String Password = (String)pageContext.getAttribute("Password");

out.println("Name = "+Name);

out.println("Password = "+ Password);

%>

</body>

</html>

PageScope3.jsp 的执行结果如图5-3 所示。

图 5-3 PageScope3.jsp 的执行结果

经过修改后的程序,Name 和Password 的值就能顺利显示出来。这个范例主要用来说明一个概

念:若数据设为Page范围时,数据只能在同一个JSP网页上取得,其他JSP网页却无法取得该数据。

5-1-2 JSP ScopeRequest

接下来介绍第二种范围:Request。Request 的范围是指在一JSP 网页发出请求到另一个JSP 网

页之间,随后这个属性就失效。设定Request 的范围时可利用request 对象中的setAttribute( )

和getAttribute( )。我们再来看下列这个范例:

RequestScope1.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - RequestScope1.jsp</title>

</head>

<body>

<h2>Request 范围 - request</h2>

<%

request.setAttribute("Name","mike");

request.setAttribute("Password","browser");

%>

<jsp:forward page="RequestScope2.jsp"/>

</body>

</html>

RequestScope2.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - RequestScope2.jsp</title>

</head>

<body>

<h2>Request 范围 - request</h2>

<%

String Name = (String) request.getAttribute("Name");

String Password = (String) request.getAttribute("Password");

out.println("Name = "+Name);

out.println("Password = "+ Password);

%>

</body>

</html>

RequestScope1.jsp 的执行结果如图5-4 所示。

现在将Name和Password的属性范围设为Request,当RequestScope1.jsp转向到RequestScope2.jsp

时,RequestScope2.jsp也能取得RequestScope1.jsp设定的Name和Password值。不过其他的JSP网页

无法得到Name和Password值,除非它们也和RequestScope1.jsp有请求的关系。

图 5-4 RequestScope1.jsp 的执行结果

除了利用转向(forward)的方法可以存取request 对象的数据之外,还能使用包含(include)的

方法。

假若我将RequestScope1.jsp 的

<jsp:forward page="RequestScope2.jsp"/>

改为

<jsp:include page="RequestScope2.jsp" flush="true"/>

执行RequestScope1.jsp 时,结果还是和图5-4 一样。表示使用<jsp:include>标签所包含进来

的网页,同样也可以取得Request 范围的数据。

5-1-3 JSP ScopeSessionApplication

表5-2 介绍了最后两种范围:Session、Application

表 5-2

范围 说 明

Session

Session 的作用范围为一段用户持续和服务器所连接的时间,但与服务

器断线后,这个属性就无效。只要将数据存入session 对象,数据的范

围就为Session

Application

Application 的作用范围在服务器一开始执行服务,到服务器关闭为

止。Application的范围最大、停留的时间也最久,所以使用时要特别注

意,不然可能会造成服务器负载越来越重的情况。只要将数据存入

application对象,数据的Scope 就为Application

表5-3 列出了一般储存和取得属性的方法,以下pageContext、request、session和application

皆可使用。

注意

pageContext 并无getAttributeNames( )方法。

表 5-3

方法 说 明

void setAttribute(String name, Object value) 设定name属性的值为value

Enumeration getAttributeNamesInScope(int

scope )

取得所有scope 范围的属性

Object getAttribute(String name) 取得name 属性的值

void removeAttribute(String name) 移除name 属性的值

当 我们使用getAttribute(String name)取得name 属性的值时,它会回传一个

java.lang.Object,因此,我们还必须根据name 属性值的类型做转换类型(Casting)的工作。例如:

若要取得String 类型的Name 属性时:

String Name = (String)pageContext.getAttribute("Name");

若是Integer 类型的Year 属性时:

Integer Year = (Integer)session.getAttribute("Year");

到目前已大约介绍完JSP 中四种范围(Scope):Page、Request、Session 和Application。假若

我的数据要设为Page 范围时,则只需要:

pageContext.setAttribute("Year", new Integer(2001));

若要为Request、Session 或Application 时,就分别存入request、session 或application

对象之中,如下:

request.setAttribute("Month", new Integer(12) );

session.setAttribute("Day", new Integer(27) );

application.setAttribute("Times", new Integer(10));

接下来就正式进入本章的主题:隐含对象(Implicit Object)。

5-2 与Servlet 有关的隐含对象

与 Servlet 有关的隐含对象有两个:page 和config。page 对象表示Servlet 本身;config 对

象则是存放Servlet 的初始参数值。

page 对象

page对象代表JSP本身,更准确地说,它代表JSP被转译后的Servlet,因此,它可以调用Servlet

类所定义的方法,不过实际上,page 对象很少在JSP 中使用。我们来看看以下的范例程序:

PageInfo.jsp

<%@ page info="JSP 2.0 技术手册" contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - PageInfo.jsp</title>

</head>

<body>

<h2>page 隐含对象</h2>

Page Info = <%= ((javax.servlet.jsp.HttpJspPage)page).getServletInfo() %>

</body>

</html>

这个例子中,我们先设定page 指令的info 属性为“JSP 2.0 技术手册”,page 对象的类型为

java.lang.Object,我们调用javax.servlet.jsp.HttpJspPage 中getServletInfo( )的方法,将

Info 打印出来,执行结果如图5-5 所示。

图 5-5 PathInfo.jsp 的执行结果

config 对象

config 对象里存放着一些Servlet 初始的数据结构,config 对象和page 对象一样都很少被用

到。config 对象实现于javax.servlet.ServletConfig 接口,它共有下列四种方法:

public String getInitParameter(name)

public java.util.Enumeration getInitParameterNames( )

public ServletContext getServletContext( )

public Sring getServletName( )

上述前两种方法可以让config对象取得Servlet 初始参数值,如果此数值不存在,就传回null。

例如:当我们在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">

<servlet>

<servlet-name>ServletConfigurator</servlet-name>

<servlet-class>

org.logicalcobwebs.proxool.configuration.ServletConfigurator

</servlet-class>

<init-param>

<param-name>propertyFile</param-name>

<param-value>

WEB-INF/classes/Proxool.properties

</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

</web-app>

那么我们就可以直接使用config.getInitParameter("propertyFile")来取得名称为

propertyFile、其值为WEB-INF/classes/Proxool.properties 的参数。如下范例:

String propertyFile = (String)config.getInitParameter("propertyFile");

5-3 与 Input / Output 有关的隐含对象

本节中,我们将介绍和Input/Output 有关的隐含对象,它们包括:out、request 和response

对象。request 对象表示客户端请求的内容;response 对象表示响应客户端的结果;而out 对象负

责把数据的结果显示到客户端的浏览器。

request 对象

request 对象包含所有请求的信息,如:请求的来源、标头、cookies和请求相关的参数值等等。

在JSP 网页中,request 对象是实现javax.servlet.http.HttpServletRequest 接口的,

HttpServletRequest 接口所提供的方法,可以将它分为四大类:

(1) 在5-1-3 小节提到的储存和取得属性方法;

(2) 能够取得请求参数的方法,如表5-4:

表 5-4 取得请求参数的方法

方 法 说 明

String getParameter(String name) 取得name 的参数值

Enumeration getParameterNames( ) 取得所有的参数名称

String [] getParameterValues(String name) 取得所有name 的参数值

Map getParameterMap( ) 取得一个要求参数的Map

(3) 能够取得请求HTTP 标头的方法,如表5-5:

表 5-5 取得请求标头的方法

方 法 说 明

String getHeader(String name) 取得name 的标头

Enumeration getHeaderNames() 取得所有的标头名称

Enumeration getHeaders(String name) 取得所有name 的标头

int getIntHeader(String name) 取得整数类型name 的标头

long getDateHeader(String name) 取得日期类型name 的标头

Cookie [] getCookies( ) 取得与请求有关的cookies

(4) 其他的方法,例如:取得请求的URL、IP 和session,如表5-6:

表 5-6 其他请求的方法

方 法 说 明

String getContextPath( ) 取得Context 路径(即站台名称)

String getMethod( ) 取得HTTP 的方法(GET、POST)

String getProtocol( ) 取得使用的协议(HTTP/1.1、HTTP/1.0 )

String getQueryString( )

取得请求的参数字符串,不过,HTTP的方

法必须为GET

String getRequestedSessionId( ) 取得用户端的Session ID

String getRequestURI( )

取得请求的URL,但是不包括请求的参数

字符串

String getRemoteAddr( ) 取得用户的IP 地址

String getRemoteHost( ) 取得用户的主机名称

int getRemotePort( ) 取得用户的主机端口

String getRemoteUser( ) 取得用户的名称

void etCharacterEncoding(String

encoding)

设定编码格式,用来解决窗体传递中文的

问题

我们来看下面这个程序范例,相信对读者会更加有帮助。

Request.html

<html>

<head>

<title>CH5 - Request.html</title>

<meta http-equiv="Content-Type" content="text/html; charset=GB2312">

</head>

<body>

<form action="Request.jsp" method="GET">

Name:<input type="text" name="Name" size="20" maxlength="20"><br>

Number:<input type="text" name="Number" size="20" maxlength="20"><br><br>

<input type="submit" value="传送">

</form>

</body>

</html>

Request.html的执行结果如图5-6 所示,笔者在Name 的字段中输入browser;Number字段中输

入123456789。

图 5-6 Request.html 的执行结果

Request.jsp

<%@ page language="java" contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Request.jsp</title>

</head>

<body>

<h2>javax.servlet.http.HttpServletRequest 接口所提供的方法</h2>

getParameter("Name"):<%= request.getParameter("Name") %><br>

getParameter("Number"):<%= request.getParameter("Number") %><br>

getAttribute("Name"):<%= request.getAttribute("Name") %><br>

getAttribute("Number"):<%= request.getAttribute("Number") %><br><br>

getAuthType( ):<%= request.getAuthType() %><br>

getProtocol( ):<%= request.getProtocol() %><br>

getMethod( ):<%= request.getMethod() %><br>

getScheme( ):<%= request.getScheme() %><br>

getContentType( ):<%= request.getContentType() %><br>

getContentLength( ):<%= request.getContentLength() %><br>

getCharacterEncoding( ):<%= request.getCharacterEncoding() %><br>

getRequestedSessionId( ):<%= request.getRequestedSessionId() %><br><br>

getContextPath( ):<%= request.getContextPath() %><br>

getServletPath( ):<%= request.getServletPath() %><br>

getPathInfo( ):<%= request.getPathInfo() %><br>

getRequestURI( ):<%= request.getRequestURI() %><br>

getQueryString( ):<%= request.getQueryString() %><br><br>

getRemoteAddr( ):<%= request.getRemoteAddr() %><br>

getRemoteHost( ):<%= request.getRemoteHost() %><br>

getRemoteUser( ):<%= request.getRemoteUser() %><br>

getRemotePort( ):<%= request.getRemotePort() %><br>

getServerName( ):<%= request.getServerName() %><br>

getServerPort( ):<%= request.getServerPort() %><br>

</body>

</html>

Request.jsp 的执行结果如图5-7 所示。

图 5-7 Request.jsp 的执行结果

在Request.jsp中,使用request.getParameter("Name")和request.getParameter("Number"),

能够取得Request.html窗体的值。除了取得请求的参数值之外,也能取得一些相关信息,如:使用的

协议、方法、URI 等等。

response 对象

response 对象主要将JSP 处理数据后的结果传回到客户端。response 对象是实现

javax.servlet.http.HttpServletResponse 接口。表5-7、表5-8、表5-9列出了response对象的

方法。

表 5-7 设定表头的方法

方 法 说 明

void addCookie(Cookie cookie) 新增cookie

void addDateHeader(String name, long

date)

新增long类型的值到name标头

void addHeader(String name, String value)

新增String 类型的值到name 标

void addIntHeader(String name, int value)新增int 类型的值到name 标头

void setDateHeader(String name, long

date)

指定long类型的值到name标头

void setHeader(String name, String value)

指定String 类型的值到name 标

void setIntHeader(String name, int value)指定int 类型的值到name 标头

表 5-8 设定响应状态码的方法

方 法 说 明

void sendError(int sc) 传送状态码(status code)

void sendError(int sc, String

msg)

传送状态码和错误信息

void setStatus(int sc) 设定状态码

表 5-9 用来URL 重写(rewriting)的方法

方 法 说 明

String encodeRedirectURL(String

url)

对使用sendRedirect( )方法的URL 予以

编码

有时候,当我们修改程序后,产生的结果却是之前的数据,执行浏览器上的刷新,才能看到更

改数据后的结果,针对这个问题,有时是因为浏览器会将之前浏览过的数据存放在浏览器的cache

中,所以当我们再次执行时,浏览器会直接从cache 中取出,因此,会显示之前旧的数据。笔者将

写一个Non-cache.jsp 程序来解决这个问题。

Non-cache.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Non-cache.jsp</title>

</head>

<body>

<h2>解决浏览器 cache 的问题 - response</h2>

<%

if (request.getProtocol().compareTo("HTTP/1.0") == 0)

response.setHeader("Pragma", "no-cache");

else if (request.getProtocol().compareTo("HTTP/1.1") == 0)

response.setHeader("Cache-Control", "no-cache");

response.setDateHeader("Expires", 0);

%>

</body>

</html>

先用request 对象取得协议,如果为HTTP/1.0,就设定标头,内容为setHeader ("Pragma",

"no-cache");若为HTTP/1.1,就设定标头为response.setHeader ("Cache-Control", "no-cache"),

最后再设定response.setDateHeader("Expires", 0)。这样Non-cache.jsp 网页在浏览过后,就不

会再存放到浏览器或是proxy 服务器的cache 中。表5-10 列出了HTTP/1.1 Cache-Control 标头的

设定参数:

表 5-10 HTTP/1.1 Cache-Control 标头的设定参数

参 数 说 明

public

数据内容皆被储存起来,就连有密码保护的网页也是一样,

因此安全性相当低

private

数据内容只能被储存到私有的caches,即non-shared caches

no-cache

数据内容绝不被储存起来。proxy 服务器和浏览器读到此标

头,就不会将数据内容存入caches 中

no-store

数据内容除了不能存入caches中之外,亦不能存入暂时的磁

盘中,这个标头防止敏感性的数据被复制

must-revalidate

用户在每次读取数据时,会再次和原来的服务器确定是否为

最新数据,而不再通过中间的proxy 服务器

proxy-revalidate

这个参数有点像must-revalidate,不过中间接收的proxy

服务器可以互相分享caches

max-age=xxx

数据内容在经过xxx秒后,就会失效,这个标头就像Expires

标头的功能一样,不过max-age=xxx只能服务HTTP/1.1 的用

户。假设两者并用时,max-age=xxx 有较高的优先权

有时候,我们想要让网页自己能自动更新,因此,须使用到Refresh 这个标头。举个例子,我

们告诉浏览器,每隔三分钟,就重新加载此网页:

response.setIntHeader("Refresh" , 180)

如果想要过十秒后,调用浏览器转到http://Server/Path 的网页时,可用如下代码:

response.setHeader("Refresh","10; URL=http://Server/Path" )

如果大家对HTML 语法还熟悉,则HTML 语法中也有类似的功能:

<META HTTP-EQUIV="Refresh" CONTENT=" 10; URL=http://Server/Path" >

上述两种方法皆可以做到自动重新加载。

out 对象

out 对象能把结果输出到网页上。通常我们最常使用out.println(String name)和out.print(String

name),它们两者最大的差别在于println( )在输出的数据后面会自动加上换行的符号,例如:你在

Dos Console的窗口下,发现到它输出数据后会自动换行;反之,print( )不会在数据后自动换行。

out 对象除了这两种方法最常使用之外,它还有一些方法(见表5-11),这些方法主要是用来控

制管理输出的缓冲区(buffer)和输出流(output stream)。

表 5-11 out 对象方法

方 法 说 明

void clear( ) 清除输出缓冲区的内容

void clearBuffer( ) 清除输出缓冲区的内容

void close( ) 关闭输出流,清除所有的内容

int getBufferSize( ) 取得目前缓冲区的大小(KB)

int getRemaining( ) 取得目前使用后还剩下的缓冲区大小(KB)

boolean isAutoFlush( )

如果回传为true,表示如缓冲区满了,会自动清除;

若为false,表示如果缓冲区满了,不会自动清除,

而会产生异常处理

我们在这里举个例子,说明如何知道目前输出缓冲区的大小。

Out.jsp

<%@ page contentType="text/html;charset=GB2312" %>

<html>

<head>

<title>CH5 - Out.jsp</title>

</head>

<body>

<h2>javax.servlet.jsp.JspWriter - out 对象</h2>

<%

int BufferSize = out.getBufferSize();

int Available = out.getRemaining();

int Used = BufferSize - Available;

%>

BufferSize : <%= BufferSize %><br>

Available : <%= Available %><br>

Used : <%= Used %><br>

</body>

</html>

BufferSize是一开始默认缓冲区的大小,默认值为8KB;Available 则是表示经过程序的执行,

目前缓冲区还剩下多少可以使用;而Used 则表示我们使用了多少的缓冲区。Out.jsp 执行结果如图