JSP&Servlet6(二) --- JSP標準標籤

什麼是標準標籤? 標準標籤有什麼作用?

簡明扼要的來說, 標準標籤就是一些以”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的設置可以是一個抽象類, 也可以是一個接口.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章