第二章 Jsp基本语法
本章簡介
??本章將系統介紹JSP頁面元素以及內置對象,其中重點介紹了out、request、 response、session等常用內置對象以及Cookie等使用,并且從使用原理上講 解了pageContext、request、session、application等四種范圍對象的作用域。
回顧第一個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()和<%=… >不僅能輸出變量,還可解析”<br/>”等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>運行結果:
②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個內置對象,如下表
| page | Context javax.servlet.jsp.PageContext | JSP頁面容器 |
| request | javax.servlet.http.HttpServletRequest | 客戶端向服務端發送的請求信息 |
| response | javax.servlet.http.HttpServletResponse | 服務器端向客戶端的響應信息 |
| session | javax.servlet.http.HttpSession | 客戶端與服務器端的一次會話 |
| application | javax.servlet.ServletContext | 可存放全局變量,實現用戶間數據的共享 |
| config | javax.servlet.ServletConfig | 服務器配置信息,可以取得初始化參數 |
| out | javax.servlet.jsp.JspWriter | 向客戶端輸出內容 |
| page | java.lang.Object | 當前JSP頁面本身,類似于Java類中的this關鍵字. |
| exception | java.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>運行結果:
若要實現換行,必須借助于HTML的<br/>或者</br>標簽
out2.jsp
<body><%out.println("hello</br>");out.print("world"); %> </body>運行結果:
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
運行結果:
??上述代碼中,通過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-8String name = request.getParameter("uname");name = new String(name.getBytes("ISO-8859-1"), "UTF-8");//將pwd的編碼方式,從ISO-8859-1轉為UTF-8String 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指定為第二項,如圖
??之后,我們只需要在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
若登陸成功,則跳轉到成功提示頁: 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”)的區別,經常會在面試中被提到,我們在此做一個總結,如下表:
| 請求服務器次數 | 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存nameCookie cookie2 = new Cookie("age","15");//創建一個cookie存ageresponse.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里點擊超鏈接,運行結果:
??可以發現,temp.jsp中的超鏈接并沒有攜帶任何參數,但跳轉后的客戶端response_addCookie.jsp頁面卻依然能獲取到Cookie對象。這是因為,在客戶端發送的請求中(超鏈接請求、表單請求等)包含著非常豐富的內容,除了可以攜帶URL參數、表單數據意外,還會傳遞豐富的請求頭信息,如圖
??可以發現,請求頭信息中包含著多個Cookie對象,每個Cookie對象都是以“鍵=值”的形式存在的,并且鍵為JSESSIONID的Cookie對象是由服務器自動產生的。
??實際上,在客戶端每一次訪問服務器時,服務器為了區分各個不同的客戶端,就會自動在每個客戶端的Cookie里設置一個JSESSIONID,表示該客戶端的唯一標示符。
(2)補充:谷歌瀏覽器查看所有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","韓梅梅");//生成一個cookiecookie.setMaxAge(10);//設置cookie在10秒鐘后失效response.addCookie(cookie);//服務端把cookie發送給客戶端response.sendRedirect("getCookie.jsp"); %> </body>獲取cookie頁面:getCookie.jsp
<body> <%Cookie[] cookie = request.getCookies();//客戶端獲取cookieboolean 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,則運行結果:
10秒鐘之后,執行getCookie.jsp,運行結果:
即,我們可以通過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
showSession.jsp
<body> <%out.print("sessionID:"+session.getId()+"</br>");//輸出sessionIDCookie[] cookies = request.getCookies();out.print(cookies[0].getName()+":"+cookies[0].getValue()); %> </body>運行結果:
下面通過一個登陸的案例來演示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);//將用戶名添加到sessionsession.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獲取usernameif(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的幾點區別:
| 保存信息的位置 | 客戶端 | 服務器端 |
| 保存的內容 | 字符串 | 對象 |
| 安全性 | 不安全 | 安全 |
5)application
??application對象是javax.servlet.ServletContext接口的實例化對象,代表了整個web項目,所以application對象的數據可以在整個web項目中共享,用法上類似于“全局變量”的概念。application對象的常用方法如下表:
| public String getContextPath() | 獲取相對路徑(默認是"/項目名稱") |
| public String getRealPath(String path) | 獲取虛擬路徑對應的絕對路徑 |
我們先直接通過一段代碼,看一下運行結果,
applicationDemo.jsp
運行結果:
??可以發現,相對路徑就是“/項目名”,而絕對路徑是完整的本地路徑。
相對路徑和絕對路徑都可以修改,可以在第一章的“配置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對象中的屬性的作用域是“在當前自身的頁面內有效”,所以能夠正常顯示。
??但如果將上述頁面進行修改,將增加屬性放在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,運行結果:
??因為頁面從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_scope_one.jsp到request_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,運行結果:
??此外,如果我們重新打開一個瀏覽器標簽(一定要相同瀏覽器),然后在新標簽里直接輸入request_scope_two.jsp,也能獲取到數據,如圖,
??但是,如果我們換一個其他瀏覽器(比如從火狐換成IE),再次直接訪問request_scope_two.jsp,就無法再獲取到數據了。如圖是Safari瀏覽器直接運行http://localhost:8080/JspProject/session_scope_two.jsp的結果:
??我們發現,無法從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.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中獲取各個表單元素的值:
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)實現以下功能的網頁記錄器:
(2)實現IP計數器,如下:
統計用戶在站點的停留時間,如下:
總結
以上是生活随笔為你收集整理的第二章 Jsp基本语法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文章编辑数据结构课程设计
- 下一篇: 工作171:阅读账号里面的新增调用接口操