第二章 Jsp基本语法

本章简介

  本章将系统介绍JSP页面元素以及内置对象,其中重点介绍了outrequestresponsesession等常用内置对象以及Cookie等使用,并且从使用原理上讲 解了pageContextrequestsessionapplication等四种范围对象的作用域。

回顾第一个jsp程序,如下,

index.jsp

<html>
	<head>
		<title>First Web Project</title>
	</head>
	<body>
		<%
			out.print("Hello World");
		%>
	</body>
</html>

  其中 <% out.print("Hello World”); %>称为脚本。可以发现,在JSP文件中,既有HTML标签,又有JAVA代码,因此我们可以把JSP看成“嵌入JAVA的HTML代码”。

但是在Eclipse中生成的Jsp内容,如下

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

</body>
</html>

  Eclipse生成的JSP文件中,除了典型的html元素外,还有很多其他内容。这是因为JSP页面本身就可以包含多种页面元素,如脚本、HTML、指令、注释等。

2.1 JSP页面元素

1) 脚本Scriptlet

所有嵌入在HTML中的JAVA代码都必须使用scriptlet包裹起来。
在JSP中共有3种Scriptlet:<% … %><% ! …%><% =…%>

Scriptlet一般写在<body>标签中。

①<%…%>

<% …%>主要用来定义局部变量编写java语句

scriptlet.jsp

<body>
<%
   String bookName ="疯狂Java讲义";
   String author = "李刚" ;
   out.print("书名:"+bookName+"<br/>作者:"+author);
    %>
</body>

运行结果:
<%...%>
其中,out.print();是JSP页面的输出语句。

②<%! … %>

<% ! … %>主要用来定义全局变量定义方法

scriptlet2.jsp

<body>
  <%!
   public String bookName ; //定义全局变量-书名
   public String author ; //定义全局变量-作者
   
   public void initInfo(){ //定义一个方法
   	bookName = "《Java疯狂讲义》";
   	author = "李刚";
   }
   %>
   
   <%
   initInfo();//调用方法
   out.print( "书名:" + bookName + "</br>" +"作者:"+ author);
   %>
</body>

运行结果:
<%!...%>
注意:
out.print()只能写在<%…%>里面。写在<%!..%>里面会报错的。

③<%=…%>

<%= … %> 用来输出=后面的表达式的值,功能类似out.print()

<body>
<%
   String bookName ="疯狂Java讲义";
   String author = "李刚" ;
    %>
    
    <%="书名:" + bookName + "</br>" +  "作者:" + author 
    %>
</body>

运行结果同上。

从上面代码,可以发现:
out.print()和<%=… >不仅能输出变量,还可解析”&lt;br/&gt;”等html代码。
<%= …>中没有“;”

2) 指令

JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。

JSP指令写在 <%@ …%>
指令可以有很多个属性,它们以键值对的形式存在,并用逗号隔开。

JSP中的三种指令标签:

指令描述
<%@ page … %>定义网页依赖属性,比如脚本语言、error页面、缓存需求等等
<%@ include … %>包含其他文件
<%@ taglib … %>引入标签库的定义
①page指令

Page指令为容器提供当前页面的使用说明。
一个JSP页面可以包含多个page指令。

Page指令的语法格式:

<%@ page language="java" import="java.util.*" contentType="text/html; 
charset=UTF-8"  pageEncoding="UTF-8"%>
属性说明
language指定JSP页面使用的脚本语言,默认是java,一般不用修改。
import与java中import的用法一致,可以执行导包操作
pageEncoding指定JSP文件本身的编码方式
contentType指定服务器发送给客户端的内容的编码方式,通常与pageEncoding保持一致
errorPage指定当JSP页面发生异常时需要转向的错误处理页面
isErrorPage指定当前页面是否可以作为另一个JSP页面的错误处理页面
extends指定servlet从哪一个类继承
info定义JSP页面的描述信息
session指定JSP页面是否使用session
isThreadSafe指定对JSP页面的访问是否为线程安全
isELIgnored指定是否执行EL表达式

page.jsp

<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>page指令</title>
</head>
<body>
<%
   Date date = new Date();
	out.print(date);
    %>
</body>
</html>

运行结果:
测试page

②include指令

JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。

Include指令的语法格式如下:

<%@ include file="文件相对 url 地址" %>

include 指令中的文件名实际上是一个相对的 URL 地址。
如果您没有给文件关联一个路径,JSP编译器默认在当前路径下寻找。

③taglib指令

JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。
Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。

Taglib指令的语法:

<%@ taglib uri="uri" prefix="prefixOfTag" %>

uri属性确定标签库的位置,prefix属性指定标签库的前缀。

3) 注释

前面讲过,基本的JSP包含了HTML和JAVA两种代码。因此,JSP的注释既包括HTML的注释,又包含JAVA的注释,此外还拥有JSP自己独有的注释

注释方式说明
<!-- -->HTML注释。可以用来注释HTML代码,但要注意此种注释能通过客户端(浏览器)查看到,因此是不安全的。
<%-- --%>JSP注释。如果想让注释不被浏览器所查看到,就可以使用JSP注释。
<% //单行注释 %> <% /*多行注释 */ %>JAVA注释。<% %>中放置的是JAVA代码,所以可以在<% %>使用//和//来对其中的JAVA代码进行注释。

note.jsp

<body>
<!-- HTML注释 -->
<%--jsp注释 --%>
<% 
//java单行注释
/*
    java多行注释
*/
%>
</body>

运行结果:
注释
鼠标右键网页空白处,查看源代码,如图:
查看源码
可以发现HTML的注释能被浏览器所查看到,而JSP注释和JAVA注释不能被查看。

2.2 九大内置对象

<%
out.print("Hello World");
%>

在上面的代码中,像out这样,没有定义和实例化(new)就可以直接使用的对象,就称为内置对象。JSP还提供了9个内置对象,如下表

内置对象类型简介
pageContext javax.servlet.jsp.PageContextJSP页面容器
requestjavax.servlet.http.HttpServletRequest客户端向服务端发送的请求信息
responsejavax.servlet.http.HttpServletResponse服务器端向客户端的响应信息
sessionjavax.servlet.http.HttpSession客户端与服务器端的一次会话
applicationjavax.servlet.ServletContext可存放全局变量,实现用户间数据的共享
configjavax.servlet.ServletConfig服务器配置信息,可以取得初始化参数
outjavax.servlet.jsp.JspWriter向客户端输出内容
pagejava.lang.Object当前JSP页面本身,类似于Java类中的this关键字.
exceptionjava.lang.Throwable当一个页面在运行过程中发生了异常,就产生这个对象

1) out

out用于向客户端输出数据,最常用的是out.print();
需要注意的是,out.println()或者out.print(“\n”)均不能实现在客户端的换行功能

out.jsp

<body>
  <%
	  	         out.println("hello");
	          	 out.print("world\n");
	         	 out.print("hello world");
%>
</body>

运行结果:

out
若要实现换行,必须借助于HTML的&lt;br/&gt;或者</br>标签

out2.jsp

<body>
   <%
	  	         out.println("hello</br>");
	         	 out.print("world");
%>
</body>

运行结果:
换行out

2) request

request对象主要用于存储“客户端发送给服务端的请求信息

因此我们可以通过request对象来获取用户发送的相关数据,request对象的常用方法如下表:

方法简介
public String getParameter(String name)获取客户端发送给服务端的参数值(由name指定的唯一参数值,如单选框、密码框的值)
public String[] getParameterValues(String name)获取客户端发送给服务端的参数值(由name指定的多个参数值,如复选框的值)
public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException指定请求的编码,用于解决乱码问题
public RequestDispatcher getRequestDispatcher(String path)返回RequestDispatcher对象,该对象的forward()方法用于转发请求
public HttpSession getSession()返回和请求相关Session
public ServletContext getServletContext()获取web应用的ServletContext对象

下面通过一个简单的注册及显示功能,演示上述部分方法的使用:

register.jsp

<body>
	<form action="show.jsp" method="post">
		用户名:<input type="text" name="username"></br>
		密码:<input type="password" name="userpwd"></br>
		爱好:<input type="checkbox" name="hobby"  value="足球">足球
					<input type="checkbox" name="hobby" value="篮球">篮球
					<input type="checkbox" name="hobby" value="羽毛球">羽毛球</br>
					<input type="submit" name="注册">
	</form>
</body>

运行结果:
注册页面
show.jsp

<body>
<%
		request.setCharacterEncoding("UTF-8");//设置请求编码和页面一致,为utf-8
		String userName = request.getParameter("username");//获取用户名
		String userPwd = request.getParameter("userpwd");//获取用户密码
		String[] hobbies = request.getParameterValues("hobby");//获取爱好
		
		//将hobbies数组转化为字符串
		String userHobbies = "";
		 if(hobbies != null){
			for(int i = 0 ; i < hobbies.length ; i++){
				userHobbies =  hobbies[i]+userHobbies +  " ";
			}
		} 
%>
		用户的注册信息如下:</br>
		用户名:<%=userName %></br>
		密码:<%=userPwd %></br>
		爱好:<%=userHobbies %></br> 
</body>

运行结果:
显示页面
  上述代码中,通过request.setCharacterEncoding("UTF-8")将POST方式的编码设置为UTF-8,,并通过request.getParameter()request.getParameterValues()方法获取到了从表单传来的数据。

  需要注意的是,客户端的数据不一定必须从表单传递过来,也可以通过URL地址进行传递,格式如下:

页面地址?参数名1=参数内容1&参数名2=参数内容2&…

  即通过“?”将页面地址和参数分离,然后按照“参数名=参数内容”的格式来传递数据,并且多个参数之间用“&”分隔。
  例如,上例中,我们可以不运行注册页register.jsp,而直接在浏览器中输入 http://localhost:8080/JspProject/register/show.jsp?username=李四&userpwd=123&hobby=足球&hobby=篮球

也能正常运行程序,并得到结果,如图
网址传参数

补充:Get与Post请求

  我们仔细观察一下表单提交和URL地址提交两种方式的地址栏,

表单提交方式的地址栏: http://localhost:8080/JspProject/register/show.jsp

URL地址提交方式的地址栏: http://localhost:8080/JspProject/register/show.jsp?uname=李四&upwd=123&hobby=足球&hobby=篮球

  这两种地址不同的本质原因,在于表单的的提交方式,在register.jsp中有一行代码 <form action="show.jsp" method="post" >其中method就可以用来指定表单的提交方式,常用属性值有get和post两种。

当method=”post”时,表示以post方式请求表单,请求后的地址为 http://localhost:8080/JspProject/register/show.jsp

当method=”get”时,再次提交表单,则地址栏就会显示 http://localhost:8080/JspProject/register/show.jsp?username=张三&userpwd=123&hobby=足球&hobby=篮球

  因此,可以发现用get方式提交表单,实际就是通过URL地址提交的方式向服务器端发送数据。

说明:

  如果 “URL地址传递”中的值是中文,而JSP页面编码是UTF-8时,则会显示乱码。
原因是“URL地址传递”使用的是GET方式传递数据,而GET方式的默认编码是ISO-8859-1,与JSP页面编码UTF-8不一致。
解决方法就是将GET方式提交的数据,进行统一字符编码,详见后文。

  除了地址栏的不同以外,get和post方式在提交的数据大小上也有区别。因为get方式会在地址栏上显示数据信息,而地址栏中能容纳的信息长度是有限制的(一般是4-5KB); 与之不同的是,post方式不会在地址栏中显示数据信息,所以能提交更多的数据内容。因此,如果表单中有一些大文本、图文、文件、视频等数据,就必须使用post的方式提交。 request的其余方法,我们会在后面详细讲述。

补充:统一字符集编码

  了解完get方式和post方式的区别后,我们再来看看两种方式如何解决字符乱码问题。

解决Web项目乱码问题的基本步骤如下(以将编码统一为UTF-8为例):

1.将所有JSP文件的编码设置为UTF-8,如下,
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html;
 charset=UTF-8">
…

此步骤,也可通过Eclipse来设置,详细步骤参见第一章。

2.对于GET或POST方式,实施不同的统一编码操作。

我们首先要知道tomcat服务器,默认使用的编码方式是ISO-8859-1。

(1)如果是以get方式提交表单(或URL地址传递的方式),则有两种方式处理编码:

方法①:分别把每一个变量的编码方式,从ISO-8859-1转为UTF-8

如以下代码:

 //将name的编码方式,从ISO-8859-1转为UTF-8
 String name = request.getParameter("uname");
 name = new String(name.getBytes("ISO-8859-1"), "UTF-8");
 //将pwd的编码方式,从ISO-8859-1转为UTF-8
 String pwd = request.getParameter("upwd");
 pwd = new String(pwd.getBytes("ISO-8859-1"), "UTF-8");

方法②:修改tomcat配置
  一次性的,将所有通过get方式传递的变量编码都设置为UTF-8(推荐)。具体修改如下:打开tomcat的conf目录,在server.xml的64行附近的元素中,加入URIEncoding=”UTF-8”,如下

server.xml

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"  URIEncoding="UTF-8" />

说明:

要使修改的server.xml生效,必须把Eclipse的tomat服务器设置成本地Tomcat托管模式,设置托管模式方法如下:
  我们使用Eclipse配置完Tomcat后,会在左侧项目导航栏多出一个Servers项目,该项目中就有Tomcat的一些配置文件,如context.xml,server.xml等。为了使Servers项目中的配置文件,与我们本地安装的Tomcat目录中的配置文件保持一致,我们可以双击控制台Servers下的Tomcat V9.0。在双击后打开的页面里,将Server Locations指定为第二项,如图
Tomact托管模式
  之后,我们只需要在Servers项目中修改配置文件,修改结果就会同步到我们本地安装的Tomcat配置文件中。因此,以后如果要对Tomcat进行操作,就只需要对Servers项目进行操作。

注意,如果发现Server Locations中的选项是灰色不可选,则需要将现有的Tomcat从Servers面板中删除,然后重新创建Tomcat服务后再选择。

(2)如果是以post方式提交表单,可以通过在服务器端加入request.setCharacterEncoding(“UTF-8”)来设置编码,详见前面的show.jsp。

3) response

我们已经知道,客户端可以通过request向服务端发送请求数据,那反过来呢?
当服务器端接收到请求的数据后,如何向客户端响应呢?答案就是response,即服务端可以通过response向客户端做出响应

response也提供了一些方法用来处理响应,如下表所示,

方法简介
public void addCookie(Cookie cookie)服务端向客户端增加Cookie对象
public void sendRedirect(String location) throws IOException将客户端发来的请求,重新定位(跳转)到另一个URL上(习惯上称为“重定向”)
public void setContentType(String type)设置服务器端响应的contentType类型

  我们先来了解一下重定向方法sendRedirect()的使用。 这次我们实现一个登陆功能:用户输入用户名和密码,如果验证正确,则跳转到欢迎页,如下:

login.jsp(输入用户名:admin,密码:123)

<body>
	<form action="check.jsp"  method="post">
		用户名:<input type="text" name="username"></br>
		密码:<input type="password" name="userpwd"></br>
		<input type="submit" name="登录">
	</form>
</body>

运行结果:
登录表单
验证页: check.jsp

<%
		String userName = request.getParameter("username");//获取用户名
		String userPwd = request.getParameter("userpwd");//获取用户密码
		if("admin".equals(userName) && "123".equals(userPwd)){//判断用户输入的用户名或者密码是否匹配
			response.sendRedirect("success.jsp");//验证成功,跳转到成功界面
		}else{
			response.sendRedirect("fail.jsp");//验证失败,跳转到失败界面
		}
%>

若登陆成功,则跳转到成功提示页: success.jsp

<body>
<%
		String userName = request.getParameter("username");
%>
欢迎你,<%=userName%>
</body>

运行结果:
成功界面
从“运行结果”可以发现两点:

1.如果用户名和密码验证成功,确实跳转到了欢迎页success.jsp,但数据却丢失了,用户名name的值为null。
2.重定向到success.jsp后,地址栏也变成了success.jsp页面的地址。

补充:请求转发与重定向

send:发送
redirect:改变方向
request:请求
dispatcher:调度员

  为了解决重定向以后数据丢失的问题,我们先来回忆一下request对象中的一个方法: public RequestDispatcher getRequestDispatcher(String path)

之前说过,此方法的返回值为RequestDispatcher(请求调度员)对象,有一个forward()方法可以用于转发请求,也就是说,request的getRequestDispatcher()方法和response的sendRedirect()方法有相同之处:都可以实现页面之间的跳转。

我们将check.jsp中的response.sendRedirect("success.jsp")改为request.getRequestDispatcher("success.jsp").forward(request, response),其他代码均不变,再次运行程序,可以看到success.jsp的结果如图:
成功页面
  可以发现, 采用了request.getRequestDispatcher("success.jsp").forward(request, response)来跳转页面后:

1.成功页面就可以获取到客户端发送的表单数据;
2.页面内容确实跳转到了success.jsp中编写的内容,但地址栏却仍然停留在check.jsp,即采用请求转发方式,地址栏不会发生改变。

  关于请求转发(request.getRequestDispatcher(“xx”).forward(request, response))和重定向response.sendRedirect(“xx”)的区别,经常会在面试中被提到,我们在此做一个总结,如下表:

请求转发(forward())重定向(redirect())
请求服务器次数1次2次
是否保留第一次请求时request范围中的属性保留不保留
地址栏里的请求URL,是否改变不变改变为重定向之后的新目标URL。
关于“请求服务器次数”的问题,再做以下详尽分析:

请求转发:客户端(浏览器)向服务器的资源A发起一次请求,服务器的资源A接收到该请求后,将该请求转发到内部的其他资源B,资源B处理完请求后,最终给客户端做出响应。

重定向:客户端(浏览器)向服务器的资源A发起一次请求,服务器的资源A接收到该请求后,给客户端做出响应,告诉客户端去重新访问资源B的地址 ,客户端收到资源B的地址后再次向服务器的资源B发出第二次请求,服务器资源B处理完该请求并做出响应。

情景模拟:

请求转发:张三去银行的A窗口办理业务,A窗口的业务员发现该业务自己办不了,就将张三的业务请求转发给其他同事办理,最后将办理完的业务返回给张三。也就是说,张三只是给银行的A窗口发送了一次请求,而该业务办理人员之间的换人工作,是银行内部处理的。即张三只发出了一次请求,更换窗口业务员(跳转)是银行的行为。

重定向:张三去银行的A窗口办理业务,A窗口的业务员发现该业务自己办不了,然后告诉张三应该重新去窗口B办理,张三收到该消息后,又重新向银行的窗口B再次请求办理业务,最终银行的窗口B处理完张三的请求,并将办理完的业务返回给张三。也就是说,张三分别向银行的窗口A、窗口B各发送了一次请求(共2次请求),更换窗口业务员(跳转)是张三的行为。

4)cookie和内置对象session

  在学习session之前,我们有必要先来了解一下cookie。
  注意:cookie不是内置对象

① cookie

  cookie对象是先由服务端产生,再发送给客户端(浏览器)的,并且浏览器会将该cookie保存在客户端的某个文件中。也就是说,cookie技术能将服务器端的一些数据,保存在用户使用的客户端计算机中。这样一来,用户下次就可以直接通过自己的计算机访问到该数据,而不必再访问服务器。因而cookie技术可以提高网页处理的效率,也能减少服务器端的负载。但是由于cookie是服务器端保存在客户端的信息,所以其安全性相对较差。

(1)cookie的使用:

  一个Cookie对象包含一个键值对,即key=value。cookie不是JSP的内置对象,需要通过JSP提供的javax.servlet.http.Cookie类来创建,Cookie类提供的常用方法如下表:

方法简介
public Cookie(String name, String value)构造方法,用来实例化Cookie对象,同时设置Cookie对象的属性名和属性值
public String getName()获取Cookie对象的名称
public String getValue()获取Cookie对象的内容
public void setMaxAge(int expiry)设置Cookie的保存时间,以秒为单位

  服务器端可以通过response对象的addCookie()方法,将Cookie对象设置到客户端;
而客户端也可以通过request对象的getCookies()方法来获取全部的Cookie对象,如下:

服务器端 response_addCookie.jsp:

<body>
<%
		Cookie cookie1 = new Cookie("name","zhangSan");//创建一个cookie存name
		Cookie cookie2 = new Cookie("age","15");//创建一个cookie存age
		
		response.addCookie(cookie1);//服务端将cookie1添加到客户端
		response.addCookie(cookie2);//服务端将cookie2添加到客户端
		
		response.sendRedirect("temp.jsp");
%>

跳转页面 temp.jsp

<body>
<a href="request_getCookie.jsp">跳转到客户端</a>
</body>

客户端 request_getCookie.jsp

<body>
<%
		Cookie[] cookies = request.getCookies();
		for(int i = 0 ; i< cookies.length; i++){
			out.print(cookies[i].getName()+","+cookies[i].getValue()+"   ");
		}
%>
</body>

  先执行response_addCookie.jsp,并在跳转后的页面temp.jsp里点击超链接,运行结果:
cookie演示

  可以发现,temp.jsp中的超链接并没有携带任何参数,但跳转后的客户端response_addCookie.jsp页面却依然能获取到Cookie对象。这是因为,在客户端发送的请求中(超链接请求、表单请求等)包含着非常丰富的内容,除了可以携带URL参数、表单数据意外,还会传递丰富的请求头信息,如图

请求头信息
  可以发现,请求头信息中包含着多个Cookie对象,每个Cookie对象都是以“键=值”的形式存在的,并且键为JSESSIONID的Cookie对象是由服务器自动产生的。

  实际上,在客户端每一次访问服务器时,服务器为了区分各个不同的客户端,就会自动在每个客户端的Cookie里设置一个JSESSIONID,表示该客户端的唯一标示符。

(2)补充:谷歌浏览器查看所有cookie信息

设置–>高级–>隐私设置和安全性–>网站设置–>cookie–>所有 Cookie 和网站数据
cookie信息

(3)通过Cookie来实现一个简单的“记住用户名”功能:

登录页login_cookie.jsp

<body>
<%!
		String username ;
		String pwd;
%>
<%
		Cookie[] cookies = request.getCookies();
		if(cookies!=null){
			for ( int i = 0 ; i< cookies.length; i++){
				if("username".equals(cookies[i].getName())){
					username = cookies[i].getValue();
				}
				if("pwd".equals(cookies[i].getName())){
					pwd = cookies[i].getValue();
				}
			}
		}
%>
	<form action="check.jsp">
		<input type="text" name="username" value="<%=username == null ?"":username%>"></br> 
		<input type="password" name="pwd" value="<%=pwd == null ?null :pwd%>"></br> 
		<input type="submit" value="登录">
	</form>
</body>

登录验证页check_cookie.jsp

<body>
<%
		String username = request.getParameter("username");//获取用户名
		String pwd = request.getParameter("pwd");//获取密码
		
		Cookie cookie = new Cookie("username",username);
		Cookie cookie2 = new Cookie("pwd",pwd);
		response.addCookie(cookie);
		response.addCookie(cookie2);
%>
</body>

运行结果:

第一次访问登录页login_cookie.jsp:
第一次登陆
输入用户名“张三”及密码并点击登录,之后如果再次访问登录页login_cookie.jsp,就会看到页面已经保存了用户名和密码,如图
第二次登陆

(5)cookie的有效期

  需要注意的是,Cookie在客户端保存的时间不是永久性的,它也是有生命周期的,我们可以通过setMaxAge(int expiry)方法设置cookie的有效期。
  例如以下代码,我们先通过setCookieAge.jsp页面设置一个Cookie对象,然后再尝试通过getCookie.jsp页面来获取该Cookie对象,

生成cookie页面:setCookieAge.jsp

<body>
<%
		Cookie cookie = new Cookie("user","韩梅梅");//生成一个cookie
		cookie.setMaxAge(10);//设置cookie在10秒钟后失效
		response.addCookie(cookie);//服务端把cookie发送给客户端
		response.sendRedirect("getCookie.jsp");
%>
</body>

获取cookie页面:getCookie.jsp

<body>
<%
		Cookie[] cookie = request.getCookies();//客户端获取cookie
		boolean flag = false;//cookie存在的标识,false为失效,true为有效
		for(int i = 0; i< cookie.length; i++){
			if("user".equals(cookie[i].getName())){
				out.print("欢迎你,"+cookie[i].getValue());
				flag = true;//把cookie设置为有效
			}
		}
		
		if(!flag){
			out.print("cookie失效了!");
		}
%>
</body>

先执行setCookieAge.jsp来设置Cookie对象。之后,如果在10秒以内运行getCookie.jsp,则运行结果:
cookie生效
10秒钟之后,执行getCookie.jsp,运行结果:
cookie失效

即,我们可以通过setMaxAge(秒数)来设置Cookie对象的有效期。

2)session

  session通常被翻译成“会话”。一个会话是指:用户通过浏览器(客户端)与服务器之间进行的一系列的交互过程,交互期间可以包含浏览器与服务器之间的多次请求、响应。以下是3个常见的session使用情景:

①用户在浏览某个网站时,从进入网站到关闭这个网站所经过的这段时间,也就是用户浏览这个网站的整个过程,就是一个session。

②在电子邮件应用中,从一个客户登录到电子邮件系统开始,经过收信、写信和发信等一系列操作,直至最后退出邮件系统,整个过程为一个session。

③在购物网站应用中,从一个客户开始购物,到浏览商品、结算等,直至最后的结账,整个过程为一个session。

session运行机制:

  当用户(浏览器)向Web应用第一次发送请求时,服务器会创建一个session对象并分配给该用户;该session对象中包含着一个唯一标识符sessionId,并且服务器会在第一次响应用户时,将此sessionId作为jsessionid保存在浏览器的Cookie对象中;这个session将一直延续到用户访问结束(浏览器关闭或用户长时间不访问Web应用)。

客户端(用户)每次都是带着自己的jsessionid去访问服务端。
①当客户端没有jsessionid的时候,服务端会创建一个session对象,然后将这个session对象发送给客户端,客户端会用jsessionid去保存这个session对象,存在cookie中。
②当客户端有jsessionid的时候,服务端就会通过客户端传来的jsessionId找到对应的session,以确定是这个用户在访问服务器。

  如果客户端禁用了Cookie,则服务器会自动使用URL-rewriting(URL重写,URL中包含session ID的信息)的技术来保存sessionId。

  session内置对象是javax.servlet.ServletContext.HttpSession接口的实例化对象,常用方法如下表:

方法简介
public String getId()获取sessionId
public boolean isNew()判断是否是新的session(新用户)
public void invalidate()使session失效
public void setAttribute(String name, Object value)设置session对象名和对象值
public Object getAttribute(String name)根据session对象名,获取session对象值
public void setMaxInactiveInterval(int interval)设置session的有效非活动时间, 单位是秒
public int getMaxInactiveInterval()获取session的有效非活动时间,单位是秒

  下面来验证一下服务端生成的sessionId和客户端生成的JSESSIONID相同。
createSession.jsp

<body>
<%
		session.setAttribute("username", "lee");
		response.sendRedirect("showSession.jsp");
%>
</body>

showSession.jsp

<body>
<%
		out.print("sessionID:"+session.getId()+"</br>");//输出sessionID
		Cookie[] cookies = request.getCookies();
		out.print(cookies[0].getName()+":"+cookies[0].getValue());
%>
</body>

运行结果:
验证sessionID和jsessionid一样

下面通过一个登陆的案例来演示session在开发中的应用:

登陆界面:login.jsp

<body>
<form action="check.jsp" method="post">
	用户姓名:<input type="text" name="username" ></br>
	用户密码:<input type="password" name="password" ></br>
	<input type="submit" value="登陆">
</form>
</body>

登陆判断:check.jsp

<body>
<%
		String username = request.getParameter("username");//获取用户名
		String password = request.getParameter("password");//获取密码
		if("admin".equals(username) && "123456".equals(password)){//验证通过
			session.setAttribute("username", username);//将用户名添加到session
			session.setMaxInactiveInterval(60*10)	;//设置session最大存活时间为10分钟
			response.sendRedirect("welcome.jsp");
		}else{//验证不通过
			response.sendRedirect("login.jsp");//重新跳转到登陆界面
		}
%>
</body>

欢迎页面:welcome.jsp

<body>
<%
		String username = (String)session.getAttribute("username");//从session获取username
		if(username == null ){
			response.sendRedirect("login.jsp");
		}else{
			out.print("欢迎您,"+ username  ) ;
			out.print("<a href='logout.jsp' >注销</a>");
		}
%>
</body>

注销页面:logout.jsp

<body>
<%
		String username = (String)session.getAttribute("username");
		if(username == null){
			response.sendRedirect("login.jsp");
		}else{
			session.invalidate();
			response.sendRedirect("login.jsp");
		}
%>
</body>

  可以发现,如果输入正确的用户名和密码(admin/123456),则直接跳转到欢迎页,如图
登陆成功
  如果用户名或密码输入有误,则返回登录页。而且,如果用户没有登录,直接访问welcome.jsp或logout.jsp,也会因为session作用域中的“username”为null而直接跳转返回登录页,从而实现访问权限的控制。

解释一下:
对于session.setMaxInactiveInterval(10);,括号中的参数单位是是秒,表示session的有效最大非活动时间是10秒钟,意思就是只要在同一个浏览器下,最后一次访问同一个项目的时间在10秒钟之内,session就不会失效。只有超过10秒钟不操作该项目,session才会失效。

最后,再说明一下cookie和session的几点区别:

cookiesession
保存信息的位置客户端服务器端
保存的内容字符串对象
安全性不安全安全

5)application

  application对象是javax.servlet.ServletContext接口的实例化对象,代表了整个web项目,所以application对象的数据可以在整个web项目中共享,用法上类似于“全局变量”的概念。application对象的常用方法如下表:

方法简介
public String getContextPath()获取相对路径(默认是"/项目名称")
public String getRealPath(String path)获取虚拟路径对应的绝对路径

我们先直接通过一段代码,看一下运行结果,
applicationDemo.jsp

<body>
<%
		String contextPath = application.getContextPath();//获取上下文路径,也就是相对路径
		String realPath = application.getRealPath("/");//获取绝对路径
		out.print("contextPath:" + contextPath);
		out.print("</br>");
		out.print("realPath:" + realPath);
%>
</body>

运行结果:
application的路径
  可以发现,相对路径就是“/项目名”,而绝对路径是完整的本地路径。
相对路径和绝对路径都可以修改,可以在第一章的“配置Web应用的虚拟路径”一节中查看。

2.3 四大作用域

  在JSP的内置对象中,包含着四种范围对象(或称为“域对象”),简介如下

对象作用域
pageContext数据只在当前自身的页面有效
request数据在一次请求中有效
session数据在一次会话中有效;但若是新开浏览器,则无效
application数据在当前Web项目有效,可供所有用户共享

以上的四个范围对象,都可以用以下的方法:

方法说明
public void setAttribute(String name, Object o)设置属性名和属性值
public Object getAttribute(String name)根据属性名,获取对应的属性值
public void removeAttribute(String name)根据属性名,删除对应的属性值

1) pageContext作用域

  我们创建一个页面 pageDemo.jsp,然后通过pageContext.setAttribute()添加两个属性(每个属性都由键值对组成),再通过pageContext.getAttribute()将属性的值取出,代码如下:

<body>
<%
		//设置pageContext属性
		pageContext.setAttribute("username", "admin");
		pageContext.setAttribute("password", "123");
%>
	用户:<%=pageContext.getAttribute("username") %></br>
	密码:<%=pageContext.getAttribute("password") %>
</body>

运行结果:
pageContext
  因为pageContext对象中的属性的作用域是“在当前自身的页面内有效”,所以能够正常显示。
  但如果将上述页面进行修改,将增加属性放在page_scope_one.jsp中执行,再通过请求转发跳转到page_scope_two.jsp页面,并在page_scope_two.jsp中显示属性的值,如下代码:

page_scope_one.jsp

<body>
<%
		//设置pageContext属性
		pageContext.setAttribute("username", "admin");
		pageContext.setAttribute("password", "123");
		request.getRequestDispatcher("page_scope_two.jsp").forward(request, response);
%>
</body>

page_scope_two.jsp

<body>
姓名:<%=pageContext.getAttribute("usernmae") %></br>
密码:<%=pageContext.getAttribute("password") %>
</body>

  执行page_scope_one.jsp,运行结果:
pageContext②
  因为页面从page_scope_one.jsp,通过请求转发跳转到page_scope_two.jsp后,就已经不再是同一个页面了,所以无法再通过pageContext对象获取到数据。

2) request作用域

  要想在请求转发后的page_scope_two.jsp页面获取到属性值,可以使用request的作用域。
request的作用域是“在客户端向服务器端,发送的一次请求中有效”。我们将上面的例子修改如下:

request_scope_one.jsp

<body>
<%
		//设置request属性
		request.setAttribute("username", "admin");
		request.setAttribute("password", "123");
		request.getRequestDispatcher("request_scope_two.jsp").forward(request, response);
%>
</body>

request_scope_two.jsp

<body>
姓名:<%=request.getAttribute("username") %></br>
密码:<%=request.getAttribute("password") %>
</body>

执行request_scope_one.jsp,运行结果:
request运行结果
  因为从request_scope_one.jsprequest_scope_two.jsp的跳转是“请求转发”,即仍然是同一次请求,而request的作用范围就是“在一次请求中有效”。

3) session作用域

  如果希望在增加属性以后,能够在跳转后的任何页面(无论是请求转发、重定向或超链接跳转),甚至是项目中任何一个页面都能获取到该属性值,就可以使用session的作用域来实现。

现在将上例的作用域从request改为session,如以下代码:

session_scope_one.jsp

<body>
  <%
   session.setAttribute("username","admin");
   session.setAttribute("password","1234");
   response.sendRedirect("session_scope_two.jsp");
  %>
</body>

session_scope_two.jsp

<body>
   书名:<%=session.getAttribute("username") %> <br/>
   作者:<%=session.getAttribute("password") %>
 </body>

执行session_scope_one.jsp,运行结果:

session作用域
  此外,如果我们重新打开一个浏览器标签(一定要相同浏览器),然后在新标签里直接输入request_scope_two.jsp,也能获取到数据,如图,
直接输入第二个页面
  但是,如果我们换一个其他浏览器(比如从火狐换成IE),再次直接访问request_scope_two.jsp,就无法再获取到数据了。如图是Safari浏览器直接运行http://localhost:8080/JspProject/session_scope_two.jsp的结果:
Safari访问
  我们发现,无法从session中获取值了。因为Safari浏览器的session并没有set书名和作者的值,所以在获取的时候才会是空值。

  我们可以联想一下平日的网购经验,如果通过谷歌浏览器登录淘宝,那么只要登录一次以后,在短时间内即使我们重新开启一个火狐标签,也会以“已登录”的身份访问淘宝;但如果换成IE浏览器,则又需要我们重新登录了。所以网站中的登录功能,就可以通过session来实现。

4) application作用域

  继续上面的讨论,如果想实现这样一个功能“只要在一个页面中增加了属性,那么即使重新换一个新浏览器,也要能访问到该属性值”,该如何实现呢?答案就是applicaton的作用域。

我们再将上例中的作用域,从session改为application,如以下代码:

application_scope_one.jsp

<body>
 <%
   application.setAttribute("username","admin");
   application.setAttribute("password","1234");
   response.sendRedirect("application_scope_two.jsp");
  %>
</body>

application_scope_two.jsp

<body>
 书名:<%=application.getAttribute("username") %> <br/>
  作者:<%=application.getAttribute("password") %>
</body>

  此外,读者可以发现,只要运行过一次application_scope_one.jsp以后,无论是新开一个浏览器标签,或者是更换新的浏览器,直接再运行application_scope_two.jsp,也都能获取到数据。如图是谷歌上执行了application_scope_one.jsp以后,在IE浏览器直接运行application_scope_two.jsp的运行结果:
application作用域
  即只要是通过application.setAttribute()增加的属性,那么任何浏览器的任何页面都可以获取到该属性值。但是如果将tomcat服务器关闭,application中的属性值就全部消失了。

我们可以利用applicatoin作用域的这一特性,来实现一个网页计数器功能:

webCounterDemo.jsp

 <body>
  <%
    Integer count = (Integer)application.getAttribute("count");//从application里面获取count值
    if(count ==  null){//如果是第一次访问,将count赋值为1 
     count = 1;
    }else{  //如果不是第一次访问,则累加一次访问次数
     count = 1 + count; 
    }
    application.setAttribute("count",count); //将访问次数的变量count保存在application的属性count中
    out.println("您是第 " + application.getAttribute("count") +" 位访问本网站的用户" );
  %>
 </body> 

  之后,无论是刷新当前页,还是新开一个浏览器标签,或者打开一个其他浏览器再次访问,每访问一次,访问次数就会累加一次。

  需要说明的是,虽然四种作用域的大小依次是pageContext<request<session<application,但我们不能为了方便就随便使用范围较大的范围对象,因为范围越大造成的性能损耗就越大。因此,如果多个作用域都能完成相同的功能,我们一般会使用范围小的那个对象。

2.4 本章练习

一、选择题

1.下列关于HTTP响应中状态码的描述,错误的是( )。(选择一项)

A.3**表示重定向,表示需要客户端采取进一步的操作才能完成请求

B.2**表示成功,表示请求已被成功接收、理解、接受

C.4**表示客户端错误,请求有语法错误或请求无法实现

D.5**表示数据库端错误,服务器未能实现合法的请求

2.下列( )方法可以获取请求的字符编码方式。(选择一项)

A.request.getCharacterEncoding()

B.request.getProtocol()

C.request.getRequestURI()

D.request.getQueryString()

3.请求转发的forward(request,response)方法是( )的方法。(选择一项)

A.request对象

B.response对象

C.RequestDispatcher对象

D.session对象

4.下列( )不是JSP九大内置对象之一。(选择一项)

A.out对象

B.exception对象

C.cookie对象

D.session对象

5.<%page language=”java” import=”java.util.ArrayList;java.sql.*” contentType=”text/html; charset=UTF-8” %>

以上指令共存在()处错误。

A:1

B:2

C:3

D:4

6.以下page指令的描述中,正确的是()。

A:可以通过<%@page include=”java.util.*”%>导入java.util下所有的类

B:可以通过<%@page include=”java.util.Date;java.util”%>导入java.util下所有的类

C:可以通过<%@page contentType=”text/html;charset=UTF-8”%>设置页面编码为UTF-8

D:可以通过<%@page version=”1.6”%>指定采用什么版本的jdk编译页面

7.以下JSP代码的运行结果是()。

A:i8

B:88

C:16

D:编译错误

<%@ page language=”java” contentType=”text/html; charset=UTF-8”%>

<%
out.println("hello lanqiao");
%>

对于以上代码,描述正确的是()。

A:此段代码没有错误,能够正确向页面打印出“hello,lanqiao!”

B:此段代码没有错误,但是不向页面输出任何语句

C:此段代码缺少引用,应该在page指令中添加import=”java.util.*”

D:此段代码存在错误,应改为:response.out.println(“hello,lanqiao!”);

9.<%@ page __=”text/html; __=UTF-8”%>

使用page指令设置页面的字符集,横线处应填写()。

A:content_Type、charsetEncoding

B:contentType、charset

C:type、charset

D:contentType、pageEncoding

10.<% ing num = 10 ;%>这段代码在JSP中称为()。

A:小脚本

B:表达式

C:JSP注释

D:JSP指令

<%
int[] scores = new int[2]{89,87};
%>
最高分:<% Math.max(scores[0],scores[1]) %>

最低分:<% out.print(Math.min(scores[0],scores[1])) %>

以上代码共有()处错误。
A:1

B:2

C:3

D:4

12.以下()不是JSP页面的page指令。

A:language=”java”

B:import=”java.util.*”

C:http-equiv=”keywords”

D:contentType=”text/html; charset=UTF-8”

13.有关会话跟踪技术描述正确的是()
A. Cookie是Web服务器发送给客户端的一小段信息,客户端请求时,可以读取该信息发送到服务器端
B. 关闭浏览器意味着会话ID丢失,但所有与原会话关联的会话数据仍保留在服务器上,直至会话过期
C. 在禁用Cookie时可以使用URL重写技术跟踪会话
D. 隐藏表单域将字段添加到HTML表单并在客户端浏览器中显示

14.在J2EE中,重定向到另一个页面,以下()语句是正确的
A. request . sendRedirect(“http:// www . baidu.com”);
B. request . sendRedirect();
C. response . sendRedirect(“http: // www . baidu.com”);
D. response .sendRedirect();

二、简答题

1.JSP中有几种Scriptlet,各自的作用是什么?

2.JSP中有几种注释,各有什么特点?

3.对于GET请求和POST请求,各如何设置编码?

4.简述pageContext、request、session、application等四个内置对象的作用域范围。

5.简述请求转发与重定向的区别。

6.如何更改Tomcat端口号。

7.简述Session和Cookie的区别。

8.什么情况下会造成“表单重复提交”?如何解决?

三、编程题

1.在一个JSP中提供一组复选框,可以让用户选择自己喜爱的水果,提交后,在另一个JSP页面输出用户的所有选择项。

2.在index.jsp中编写两个输入框,用于接收用户输入的两个数,然后通过表单提交跳转到result.jsp。再在result.jsp中比较判断出较大的数字,并显示。

3.实现以下表单,并在用户单击“注册”后,在另一个JSP中获取各个表单元素的值:

3题表格
4.<a href=”showStudents.jsp?page=2&size=10”>学生信息,获取超链接中的page、size参数值。

5.demo01.jsp的部分内容如下:

<body>
 <%
  Student stu = new Student("张三",23);
  ...
 %>
</body>

将demo01.jsp中的stu对象传递到demo02.jsp。

6.实现登陆功能,要求:在客户端保存用户名,在服务端保存登录信息。

7.禁止从外部网站提交数据(即服务端只接受本项目中传来的数据)。

8.(1)实现以下功能的网页记录器:
8题1问图片
(2)实现IP计数器,如下:
8题3问图片
统计用户在站点的停留时间,如下:
8题图片

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值