什麼是標準標籤? 標準標籤有什麼作用?
簡明扼要的來說, 標準標籤就是一些以”jsp:”作爲前置的標籤, 主要作用是爲了減少JSP中的Java代碼.
1. <jsp:include> <jsp:forward>標籤
<jsp:include>
在前面曾經提到過include指示元素, 可以在JSP轉譯的時候將其他的JSP頁面包括合併進來一起轉譯, 但是這樣include進來的JSP頁面是靜態的, 我們無法給包括進來的JSP頁面傳遞參數或對它進行動態調整, 只是將多個JSP頁面合併成一個JSP頁面再進行轉譯, 結果也只會生成一個Servlet.
但是使用標準標籤<jsp:include>可以在運行時動態地將其他的JSP頁面包括進來, 並傳遞參數等. 被包含的JSP頁面也會自己獨立生成一個Servlet類.
<jsp:include>在使用過程中實質上發生了什麼呢?
前面在Servlet裏面提到過請求的包含(include())與轉發(forward()), 其實, 在JSP轉譯生成的Servlet中, 與處理Servlet中的請求的包含與轉發是一樣的, 也是會先取得RequestDispatcher對象, 然後在執行該對象的include()/forward()方法. 但由於使用<jsp:include>時, 被包含的JSP頁面也會獨立生成一個Servlet, 所以在轉譯後的Servlet執行RequestDispatcher對象的include()方法時, 會將請求轉發給被包含的頁面所獨立生成的Servlet, 而後再回到目前的Servlet.
<jsp:include>使用範例:
下面的代碼時將add.jsp包含進當前的JSP頁面, 並使用<jsp:param>標籤傳遞給add.jsp一些參數:
<jsp:include page = "add.jsp">
<jsp:param name = "a" value = "1">
<jsp:param name = "b" value = "2">
</jsp:include>
<jsp:forward>
<jsp:forward>與<jsp:include>的用法基本相似, 示例如下:
<jsp:forward page = "add.jsp">
<jsp:param name = "a" value = "1">
<jsp:param name = "b" value = "2">
</jsp:forward>
關於Servlet裏轉發與重定向的區別
既然講到了JSP裏面的轉發請求, 那麼就來簡要的談談Servlet裏面轉發與重定向的區別吧!
例:轉發
request.getRequestDispatcher("/test.view").forword(request, response);
例:重定向
response.sendRedirect("login.html");
轉發: 轉發的整個流程都是在服務端完成的, 而且是在同一個請求中完成的, 當前Servlet和轉發的Servlet共享一個request, 在當前Servlet中放的東西, 在轉發的Servlet中都能取得到, 整個過程是一個請求, 一個響應.
重定向: 客戶發送一個請求到服務器, 服務器匹配Servlet, Servlet處理完之後調用response的sendRedirect()方法, 所以, 噹噹前的Servlet處理完之後, 會立即向客戶端返回這個響應, 響應會告訴客戶端必須要再發送一個請求去訪問login.html, 於是客戶端收到這個消息會立即發送一個新的請求去請求login.html, 這裏的2個請求互不干擾, 互相獨立. 當前的Servlet的request的setAttribute()裏的任何東西, 在重定向的頁面裏都獲取不到, 可見是2個請求, 2個響應.
2. <jsp:useBean>的使用和 <jsp:setProperty> /<jsp:getProperty>的簡介
<jsp:useBean>
既然這個標籤是<jsp:useBean>, 那就是use Bean, 那麼我們先來了解一下”Bean”是什麼.
Bean就是JavaBean, JavaBean是純Java對象, 它具由以下幾個特點:
- 首先必須實現java.io.Serializable接口;
- 沒有public類型的類變量;
- 具有無參的構造器;
- 具有公開的getter和setter方法.
下面給出一個JavaBean的代碼的例子:
public class User implements Serializable{
private String name;
private String passwd;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getPasswd(){
return passwd;
}
public void setPasswd(String passwd){
this.passwd = passwd;
}
public boolean isValid(){
return "dela".equals(name) && "123456".equals(passwd);
}
}
我們先不考慮這是不是一個JavaBean, 就先把它當成一個普通的Java類來看, 那麼在JSP中應用這個類大概會有如下代碼:
<%@page import = "com.zhuyidi.chapter6.JSPDemo.*"
contentType = "text/html" pageEncoding = "UTF-8"%>
<%
User user = (User)request.getAttribute("user");
if(user == null){
user = new User();
request.setAttribute("user", user);
}
user.setName(request.getParameter("name"));
user.setPasswd(request.getParameter("passwd"));
%>
//略...
<body>
<%
if(user.isValid()){
%>
<h1><%= user.getName()%>登錄成功</h1>
<%
}else{
%>
<h1>登錄失敗!</h1>
<%
}
%>
</body>
</html>
可以看出, 在JSP中直接編寫Scriptlet(即Java代碼)使用JavaBean非常麻煩, 在一開始也提到了, JSP的標準標籤就是爲了減少JSP中Java代碼, 所以就有了<jsp:useBean>去使用JavaBean.
下面就來看以下使用<jsp:useBean>來操作User這個JavaBean, 代碼如下:
<%@page contentType = "text/html" pageEncoding = "UTF-8"%>
<jsp:UseBean id = "bean_user" class = "com.zhuyidi.chapter6.JSPDemo.User" />
<jsp:setProperty name = "bean_user" property = "*" />
<html>
<head>
<meta http-equiv = "Content-Type" content = "text/html"; charset = "UTF-8">
<title>登錄頁面</title>
</head>
<body>
<%
if(bean_user.isValid()){
%>
<h1><jsp:getProperty name = "bean_user" property = "name" />登錄成功</h1>
<%
}else{
%>
<h1>登錄失敗</h1>
<%
}
%>
</body>
</html>
可以看出, 使用<jsp:useBean>將會大大減少Scriptlet的編寫, 那麼下面我們對上述代碼的一些關鍵字/標籤進行解釋一下吧.
- <jsp:useBean>標籤: 用來取得或創建JavaBean.
- id屬性: 指定JavaBean實例的名稱. 之後在使用<jsp:setProperty>和<jsp:getProperty>的時候通過指定name屬性, 來取得id與指定的name相同的JavaBean實例.
- class屬性: 指定實例化哪一個類.
- scope屬性: 用於指定JavaBean實例對象所存儲的域範圍. 其取值只能是page/request/session/application四個值中的一個,其默認值是page.
- <jsp:useBean>標籤: 用於設置已經實例化的JavaBean的屬性值.
- name屬性: 用於指定要設置屬性的JavaBean實例的id值.
- property屬性: 用於給JavaBean實例賦值. 由於, property屬性的賦值操作有好幾種情況, 在此鏈接一篇非常詳細的博客以便日後查閱: <jsp:setproperty>的用法
- <jsp:getProperty>標籤: 用來取得已經實例化的JavaBean的屬性值.
- name屬性: 用於指定要取得屬性值的JavaBean實例的id值.
- property屬性: 用於取得JavaBean實例的屬性值.
3. 深入<jsp:useBean>的使用和 <jsp:setProperty> /<jsp:getProperty>
<jsp:useBean>的scope屬性
其實, JavaBean就是Java的一個類, 而JavaBean的實例, 就相當於這個Java類的一個對象. JSP終將轉譯成Servlet, 舉個栗子, 有如下JSP代碼:
<jsp:useBean id = "bean_user" class = "com.zhuyidi.chapter6.JSPDemo.User" />
這行代碼轉譯爲Servlet應該是這樣的:
com.zhuyidi.chapter6.JSPDemo.User bean_user = null;
synchronized(request){
bean_user = (com.zhuyidi.chapter6.JSPDemo.User)_jspx_page_context.getAttribute("bean_user", PageContext.PAGE_SCOPE);
if(bean_user == null){
bean user = new com.zhuyidi.chapter6.JSPDemo.User();
_jspx_page_context.setAttribute("bean_user", bean_user, pageContext.PAGE_SCOPE);
}
}
這段代碼就是說, 在使用<jsp:useBean>時, 會在屬性範圍查找是否存在以id屬性爲名稱的Bean對象, 如果找到了就直接使用, 否則就創建. 其中, _jspx_page_context引用至pageContext對象.
在上面這段代碼中, 我們注意到“PAGE_SCOPE”, 這個參數的意思就是, 查找以id屬性爲名稱的Bean對象的範圍是page. 而這個參數對應至JSP中的<jsp:useBean scope = “page” />.
下面就來詳細瞭解一下scope屬性吧.
前面提到: scope屬性: 用於指定JavaBean實例對象所存儲的域範圍. 其取值只能是page/request/session/application四個值中的一個,其默認值是page. 這四個值都有什麼區別呢? 我們以一個很直觀的例子來驗證一下.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:useBean id="currentime" class="java.util.Date" scope="page" />
<html>
<head>
<title>當前時間</title>
</head>
<body>
<%= currentime.toString()%>
</body>
</html>
將上述代碼的scope屬性分別替換爲: page/request/session/application, 頁面上當前時間的顯示的結果分別如下:
- 當scope = “page”時, 每刷新一次頁面, 當前時間就更新一次. 這說明Bean實例只在當前頁面有效, 離開當前頁面就已經失效.
- 當scope = “request”時, 每刷新一次頁面, 當前時間就更新一次. 這說明Bean實例只在本次請求有效, 當刷新一次頁面必然不再是同一個請求, 若要進一步驗證, 可以使用前面提過的轉發(一次請求)和重定向(兩次請求)來驗證.
- 當scope = “session”時, 刷新頁面時間不會更新, 但是重新打開一個瀏覽器請求該JSP頁面時間就會得到更新. 在驗證過程中, 首次使用chrome瀏覽器請求該JSP頁面得到一個時間, 然後在chrome下再打開一個頁面去請求得到的時間不變; 而當我使用firefox瀏覽器再去請求該JSP頁面時, 時間得到了刷新, 然後在firefox下再打開一個頁面, 時間又沒有發生改變. 這說明, 一個打開一個新的瀏覽器就是重新建立session, 即重新建立一個Bean的實例.
- 當scope = “application”時, 不管是刷新頁面還是重新打開瀏覽器, 時間都不會發生變化. 這說明Bean的實例在內存中只有一份, 只要不重啓WEB服務, 時間都不會發生改變.
<jsp:useBean>的type屬性和class屬性的比較
關於type和class的區別, 有一篇博客給出了這樣的回答, 但是由於博主有一點筆誤, 所以我在這裏再重新總結下.
<jsp:useBean id="myBean" class="package.MyBean" scope="request" />
<jsp:useBean id="myBean" type="package.MyBean" scope="request" />
以上兩種用法, 當myBean不爲空時, 在使用上兩者沒有什麼區別. 但是當myBean被設爲null後, 例如request.setAttribute(“myBean”,null), 兩者在使用上就有區別了. 使用class=”package.MyBean”時不會拋出異常, 使用type=”package.MyBean”時會拋出異常. 原因是當使用class時, 首先在當前作用範圍內查找是否存在myBean, 如果存在則直接使用現成的, 如果不存在則new一個. 而當使用type時, 如果當前範圍內不存在myBean, 而且又沒有使用class或beanName指定type時, 就會拋出異常. 並且class與beanName必須指定package(即引入了包), 而type可以不指定. 且type的設置可以是一個抽象類, 也可以是一個接口.