JSP、Servlet 與 JavaBean 的組合應用

來源:互聯網
上載者:User
文章目錄
  • 2.1 基礎知識
  • 2.2 撰寫 JavaBean
  • 2.3 在 JSP 中使用 JavaBeans
  • 2.4 佈署與執行
  • 3.1 撰寫 servlet
  • 3.2 撰寫 JSP
  • 3.3 佈署與執行
  • 4.1 撰寫 JavaBean
  • 4.2 撰寫 Servlet
  • 4.3 撰寫 JSP
  • 4.4 佈署與執行
http://sun.cis.scu.edu.tw/~nms9115/articles/java/WebAppTutor/JspServletBean/JspServletBean.htmJSP、Servlet 與 JavaBean 的組合應用

作者:蔡煥麟
日期:Jan-22-2003
更新:Feb-4-2003 

1.0 簡介

上次的討論中,最後有一個範例是判斷質數的 JSP 程式,該程式在 JSP 中嵌入許多 Java code,我們也說過這是不好的設計方式,這次就來看看怎麼樣把這些 Java code 從 JSP 中抽離出來,成為獨立的類別(稱為 JavaBeans),並且示範如何在 JSP 裡面呼叫這些 JavaBeans 。另外,也會一併介紹由 Servlet 呼叫 JSP 的方式,之前看的範常式式,其流程、邏輯、和資料展現都放在 JSP,這種設計方式稱為 page-centric 架構,或 Model-1 架構(圖 1),現在開始撰寫的範例會將控制權交給 servlet,以 servlet 為控制中心,掌控程式的流程以及 HTML/JSP 網頁的指派,這是一種 servlet-centric 的架構,也稱為 Model-2 架構(圖 2),其實也就是 MVC(Model-View-Controller)架構的基礎。

圖 1. page-centric 架構

圖 2. servlet-centric 架構

這次的學習重點:

  • 瞭解如何設計 JavaBeans。
  • 瞭解如何在 JSP 中使用 JavaBeans(存取 JavaBeans 的屬性和方法)。
  • 瞭解 servlet 如何指派 JSP 網頁。
2.0 範例:JSP 呼叫 JavaBeans2.1 基礎知識

這裡所說的 JavaBeans 只是一般的 Java 類別,跟 EJB(Enterprise JavaBeans)是兩種不同的東西,請勿混淆了。那麼,servlet 也是 Java 類別,它跟 JavaBeans 又有什麼不同呢? 

JavaBeans 只是普通的類別

Servlet 的 Java 類別是繼承自 javax.servlet.HttpServlet,因此具有接收 HTTP request 和送出 HTTP response 等網站應用程式的準系統,而 JavaBeans 則只是單純的類別,它可以繼承自任何類別,但無法處理 HTTP 訊息,它在網站應用程式中的角色通常是作為參數物件(在 JSP 和 servlet 之間傳遞,以共用資訊)或工具類別,作為參數物件時,通常代表種資料,因此被稱為 value bean,作為工具類別時,則稱為 utility bean。

怎樣的類別可以稱為 JavaBeans?

只要你遵守 JavaBeans 規範中所建議的命名和設計慣例,而且你以 bean 的方式使用它,那麼它就可以稱為一個 bean。[1]

類別通常以 "動詞+Bean" 的方式命名,例如:UserInfoBean, CheckStockBean....等。這是一種慣例,雖然沒有強制非這樣命名不可,但是它有好處:清楚,別的程式設計師一眼就可以看出這是個 bean。

JSP 如何使用 JavaBeans?

要讓 JSP 能夠使用你的 bean,你的 bean 必須提供一組屬性,JSP 便可以透過特殊的標籤來存取這個 bean 的屬性。所謂的屬性,其實是一組 getter 和 setter methods,兩者統稱為 access methods(存取方法),透過這組存取方法來間接地存取類別的私人成員,當然,這組存取方法必須宣告為 public。例如,有個 bean 類別 EmployeeBean,它要提供一個年齡的屬性給外界(JSP)存取,此類別的定義如下:

public class EmployeeBean {private int age;public int getAge() {return age;}public void setAge(int age) {self.age = age;}}

在 JSP 裡面使用時,是這麼個寫法:

<jsp:useBean id="emp" class="com.huanlin.EmployeeBean" scope="request"/><jsp:setProperty name="emp" property="age" value="25" />員工的年齡是: <jsp:getProperty name="emp" property="age" />

其中

  • <jsp:useBean> 標籤就是指明要使用一個 bean 物件,id 代表該物件的名稱,class 指明了要使用什麼類別的 bean,scope 則代表物件的生命週期。
  • <jsp:setProperty> 標籤用來設定 bean 的屬性值,property 指明了要設定哪個屬性,value 就是數值,要別注意的是,HTML 表單傳入的資料一定是字串,但我們的 age 屬性卻是整數,這個部分的轉換會由 Web container 幫我們處理掉。
  • <jsp:getProperty> 標籤是用來取得 bean 的屬性值。

請特別注意兩點:

  1. 屬性的大小寫。在 JSP 裡面,屬性的名稱是完全小寫的 "age",但是 getter 和 setter methods 的名稱卻是 getAge() 和 setAge(),這種名稱的轉換對應規則是固定的,照這個規則來命名,Web container 就能夠找到正確的存取方法。
  2. 對於 OOP 觀念不熟的人來說,可能會誤以為在 JSP 裡面存取的 "age" 屬性,就是類別定義裡面的那個宣告為 private 成員(age),其實兩者只有字面上相同而已,實際運作是可以毫無關聯的,因為 JSP 完全是透過 getter 和 setter 方法來存取屬性,況且外界本來就無法存取類別的私人成員。

基本知識介紹到此,接下來是實作,如果有未詳盡之處,請自行參閱相關書籍。

2.2 撰寫 JavaBean

我們把上次的教學檔案的最後一個範例,也就是判斷質數的 JSP 程式拿來修改,其中的 isPrimeNumber 函式很明顯可以獨立出來(以便重複使用),放到一個類別裡面,我把這個類別命名為 CheckPrimeBean。程式碼如表 1 所示。

表 1. PrimeValidator.java

// 檔名:CheckPrimeBean.java            // 編譯:javac -d ..\classes CheckPrimeBean.java            //===============================================            package com.huanlin.util;            public class CheckPrimeBean {            private int number;            public String getNumber() {            return Integer.toString(number);    // 整數轉成字串            }            public void setNumber(String s) {            try {            number = Integer.parseInt(s);   // 字串轉成整數            }            catch (NumberFormatException e) {            number = -1;            }            }            public boolean isValidNumber() {    // 檢查輸入的數字是否合法            if ((number < 2) || (number > 10000))            return false;            return true;            }            public boolean isPrimeNumber() {    // 判斷是否為質數            for (int i = 2; i <= number/2; i++) {            if (number % 2 == 0)            return false;            }            return true;            }            }

幾點說明:

  1. 這裡使用了具名的套件(named package),套件名稱是 com.huanlin.util,表示你將來佈署的 .class 檔案也要有相同的路徑結構,也就是編譯出來的檔案及路徑名稱會是 "com\huanlin\util\CheckPrimeBean.class"。在編譯這個檔案時,編譯器會根據你的 package 名稱幫你自動建立好對應的目錄。(
  2. 在安排檔案目錄的結構時,我把原始碼和編譯過的類別檔分開目錄存放,檔案目錄結構像是這個樣子:
    sources\CheckPrimeBean.java                classes\com\huanlin\util\CheckPrimeBean.class                

    也就是這個範例的目錄下會有兩個目錄:sources 和 classes,分別存放原始碼和編譯過的檔案。因為這個緣故,在編譯時必須特別指定輸出的檔案目錄,這部分請參考表 1 的第 2 行註解。  

  3. 這個類別的使用方式,是先設定 number 這個屬性,然後呼叫 isValidNumber() 檢查輸入的數字是否為有效整數,最後才由 isPrimeNumber() 判斷是否為質數。
關於 package

你也許會發現,即使不寫 package 那行,程式也可以通過編譯,但由於這個 bean 是要用在 JSP 裡面的,如果你不為  package 命名的話,在 JSP 裡面使用這個 bean 時,Web container 會找不到這個 bean。請到相關書籍中找尋 package 的相關說明。

2.3 在 JSP 中使用 JavaBeans

原本在 JSP 裡面的一些 Java 程式碼被抽離成獨立的 CheckPrimeBean 類別之後,程式碼就清爽些了,修改後的 JSP 檔名取做  CalcPrime2.jsp,參考表 2。

表 2. CalcPrime2.jsp

<%-- 檢查某個數字是否為質數的 JSP 程式 --%>            <%@ page language="java" contentType="text/html;charset=big5" %>            <%            request.setCharacterEncoding("big5");            String num = request.getParameter("number");  // 取得 HTTP request 的參數            %>            <html>            <body>            <jsp:useBean id="checker" class="com.huanlin.util.CheckPrimeBean" scope="request"/>            <jsp:setProperty name="checker" property="number" value="<%= num %>" />            <% if (!checker.isValidNumber()) { %>            <% response.setHeader("Refresh", "5; URL=prime2.htm"); %>            請輸入 2~10000 之間的整數。<p>            五秒後將自動回到 prime2.htm。            <% return; } %>   <%-- 顯示錯誤訊息後結束,亦即後續的指令不會被處理 --%>            <% if (checker.isPrimeNumber()) { %>            <%= num %> 是質數            <% } else { %>            <%= num %> 不是質數            <% } %>            </body>            </html>

關於在 JSP 使用 bean 的方法,之前都有提過了,只有一點值得特別說明,就是 <jsp:setProperty> 這行的 value 屬性(attribute),請注意它使用了 <%= .. %> 標籤來將一個變數的值傳入 value 屬性。其實它還可以這樣寫:

<jsp:setProperty name="checker" property="number" param="number" />

也就是不明白指定 value,而改用 param 這個屬性,讓 Web container 在處理 JSP 指令時自動幫我們帶入 "number" 這個 HTML 表單傳入的參數。由於我們的 HTML 表單的參數名稱和 bean 的屬性名稱都叫做 "number",JSP 也允許我們將  param 省略不寫,像這樣:

<jsp:setProperty name="checker" property="number" />

這樣就更簡潔了。如果你覺得這樣寫語意不明,或者考慮到某些程式設計師不知道有這種寫法,那就還是把 param 寫上去好了。

2.4 佈署與執行
  1. 把 sources 目錄下的 prime2.htm 和 CalcPrime.jsp 複製到 Tomcat 的 webapps\myapp\ 目錄下。
  2. 把 classes 目錄整個複製到 Tomcat 的 webapps\myapp\WEB-INF\ 目錄下。
  3. 在瀏覽器的網址列輸入 "http://127.0.0.1:8080/myapp/prime2.htm"。

3.0 範例:Servlet 呼叫 JSP

Servlet 要呼叫(說得精確一點應該是:指派 JSP 頁面)JSP,跟在 JSP 中使用 JavaBeans 比起來要簡單多了,主要只是網頁轉送的技巧而已,此技巧在設計 圖 2 的架構時會用得著。

3.1 撰寫 servlet

Servlet 程式碼列於表 3。

表 3. HelloWorldServlet.jsp

import java.io.*;            import javax.servlet.*;            import javax.servlet.http.*;            public class HelloWorldServlet extends HttpServlet {            public void service(HttpServletRequest request,            HttpServletResponse response)            throws ServletException, IOException {            response.setContentType("text/html; charset=big5");            request.setCharacterEncoding("big5");            String theMessage = "Hello, World!";            String targetURL = "/HelloFromServlet.jsp";            request.setAttribute("message", theMessage);            RequestDispatcher rd;            rd = getServletContext().getRequestDispatcher(targetURL);            rd.forward(request, response);            }            }

程式碼有幾個地方值得特別注意:

  1. 之前的 servlet 範常式式都是用 doGet 來處理用戶端的 HTTP request,這裡則改用 service。
  2. request.setAttribute()。
  3. RequestDisatcher 類別。
  4. getServletContext()。
  5. ServletContext 類別的 getRequestDispatcher() 方法。
  6. RequestDispatcher 類別的 forward() 方法。

請試著從你手邊的書籍或網路資源中尋找相關的說明,以瞭解程式運作的原理。

3.2 撰寫 JSP

表 3. HelloFromServlet.jsp

<%@ page language="java" contentType="text/html;charset=big5" %>            <% String msg = (String)request.getAttribute("message"); %>            <html>            <body>            從 servlet 傳來的訊息: <%= msg %>            </body>            </html>

之前的 servlet 程式中有用到 request.setAttribute(),這裡則使用了 request.getAttribute(),從這裡可以看得出來,servlet 和 JSP 之間是透過 request 物件來儲存及傳遞給對方的參數。

程式的運作過程如下:

  1. 用戶端送出 HTTP request,請求的網址為 "http://127.0.0.1:8080/myapp/HelloWorldServlet"。
  2. HelloWorldServlet 收到請求之後,透過 request.setAttribute() 把要傳遞給 JSP 的參數字串 "Hello, World!" 儲存在 request 物件裡。
  3. HelloWorldServlet 透過 ServletContext 建立 RequestDispatcher 物件,並指定欲指派的網址。
  4. HelloWorldServlet 呼叫  RequestDispatcher 的 forward() 方法,把這次的 HTTP request 轉送至另一個網頁,也就是 HelloFromServlet.jsp。
  5. JSP 先透過 request.getAttribute() 取得 request 物件中的屬性值,該屬性值是在步驟 2 中,由 servlet 指定的。最後再該屬性值搭配 HTML 標籤顯示出來。

我把整個過程畫成一個 UML 循序圖(圖 3),你可以搭配上面的文字描述來瞭解程式的運作過程。

圖 3. Servlet 指派 JSP 網頁的過程(sequence diagram)

3.3 佈署與執行
  1. 在 Tomcat 的 webapps 目錄下建立目錄結構:myapp\WEB-INF\classes\。請注意大小寫是有區別的。
  2. 將 HelloFromServlet.jsp 複製到 myapp 目錄下。
  3. 編譯 HelloWorldServlet.java,並將編譯後產生的 HelloWorldServlet.class 複製到 myapp\WEB-INF\classes\ 目錄下。
  4. 編輯 myapp\WEB-INF\web.xml 檔案,內容如下:

    表 4. web.xml

    <?xml version="1.0" encoding="ISO-8859-1"?>                <!DOCTYPE web-app                PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"                "http://java.sun.com/dtd/web-app_2_3.dtd">                <web-app>                <servlet>                <servlet-name>                HelloWorldServlet                </servlet-name>                <servlet-class>                HelloWorldServlet                </servlet-class>                </servlet>                <servlet-mapping>                <servlet-name>HelloWorldServlet</servlet-name>                <url-pattern>/HelloWorldServlet</url-pattern>                </servlet-mapping>                </web-app>
  5. 開啟瀏覽器,在網址列輸入 URL "http://127.0.0.1:8080/myapp/HelloWorldServlet"。
4.0 範例:Servlet、JavaBeans、與 JSP 的組合

此範例跟 3.0 的範例其實很像,都是由 Servlet 傳遞參數給 JSP,再由 JSP 取出參數並顯示出來,只是 3.0 的範例的參數是字串,而這裡要示範的是以 JavaBeans 物件當作參數來傳遞共用資訊。

程式的目錄結構如圖 4 所示:

圖 4. 目錄結構

編譯後的 Java class 檔案都輸出至 classes 目錄下,而由於 HelloServlet2.java 需要參考 UserInfoBean.java,所以在編譯時要必須使用 -class 參數,否則會找不到類別,為了方便起見,我們用一個批次檔 Make.bat 幫我們編譯所有的 Java 類別。

4.1 撰寫 JavaBean

我們打算用一個 UserInfoBean 類別來儲存一個使用者的相關資訊,並且在 servlet 和 JSP 之間傳遞這個物件,以達到溝通和資訊共用的目的。為了示範方便,這個類別只提供了一個屬性:userName,程式碼列在表 5。

表 5. UserInfoBean.java

// 檔名:UserInfoBean.java            // 編譯:javac -d ..\classes UserInfoBean.java            package com.huanlin;            public class UserInfoBean {            private String userName;            public void setUserName(String userName) {            this.userName = userName;            }            public String getUserName() {            return this.userName;            }            }
4.2 撰寫 Servlet

表 6. HelloServlet2.jsp

// 檔案:HelloServlet2.java            // 編譯:參考 Make.bat            import java.io.*;            import javax.servlet.*;            import javax.servlet.http.*;            import com.huanlin.UserInfoBean;            public class HelloServlet2 extends HttpServlet {            public void service(HttpServletRequest request,            HttpServletResponse response)            throws ServletException, IOException {            // 下面兩行讓中文字能正確顯示            response.setContentType("text/html; charset=big5");            request.setCharacterEncoding("big5");            // 建立 userInfo 物件,並指定一個 session 的 attribute 與之繫結            UserInfoBean userInfo = new UserInfoBean();            userInfo.setUserName("令狐沖");            HttpSession session = request.getSession();            session.setAttribute("userInfo", userInfo);            // 前往指定的網頁            RequestDispatcher rd;            rd = getServletContext().getRequestDispatcher("/HelloFromServlet2.jsp");            rd.forward(request, response);            }            }

session.setAttribute() 會將 UserInfoBean 物件的參考存入 session 裡面。

4.3 撰寫 JSP
<%@ page contentType="text/html;charset=big5" %>            <jsp:useBean id="userInfo" class="com.huanlin.UserInfoBean" scope="session"/>            <html>            <body>            <p>從 servlet 傳入的 UserInfoBean.userName 是:            <b>            <jsp:getProperty name="userInfo" property="userName"/>            </b>            </body>            </html>

有個地方要特別注意,如果在 servlet 儲存參數時是呼叫 session.setAttribute() 方法,也就是將參數存入 session 中,那麼在 JSP 裡面的 <jsp:useBean> 標籤的 scope 就必須指明為 "session",否則會發生取不到參數的情形。

由於使用者登入之後,其帳號等相關資訊必須一直存在,直到這名使用者登出或將瀏覽器關閉之後才清除,因此我們把 UserInfoBean 物件存放在 session 中。一般來說,為了節省記憶體資源,非必要時不要將變數存在 session 中,如果 bean 傳送到 JSP 中用完即丟,可以將它存放在 request 裡面。

4.4 佈署與執行
  1. 執行 Make.bat 產生所有的 .class 檔。
  2. 將 Make.bat 產生的 .class 檔案,也就是整個 classes 目錄複製到 myapp\WEN-INF\classes\ 目錄下。
  3. 將 HelloFromServlet2.jsp 複製到 myapp 目錄下。
  4. 編輯 myapp\WEB-INF\web.xml 檔案,加入下列內容如下:

    表 4. web.xml

    <web-app>                <servlet>                <servlet-name>                HelloServlet2                </servlet-name>                <servlet-class>                HelloServlet2                </servlet-class>                </servlet>                <servlet-mapping>                <servlet-name>HelloServlet2</servlet-name>                <url-pattern>/HelloServlet2</url-pattern>                </servlet-mapping>                </web-app>
  5. 開啟瀏覽器,在網址列輸入 URL "http://127.0.0.1:8080/myapp/HelloServlet2"。
5.0 學習評量
  1. 解釋何謂 page-centric 和 servlet-centric 架構,並比較兩者的優缺點。
  2. 什麼是 JavaBeans?
  3. JSP 提供哪些標籤可以讓我們存取 JavaBeans 的屬性?
  4. JavaBeans 如何提供屬性讓 JSP 存取?
  5. 為什麼要使用具名的 package?有什麼好處?
  6. 表 3 的 HelloWorldServlet 類別改寫(override)了 service() 方法,這和之前改寫 doGet() 方法有什麼差別?
  7. 說明 HttpServletRequest.setAttribute() 的用途。
  8. 說明 RequestDispatcher.forward() 的用途。
  9. <jsp:useBean> 標籤的 scope 作用是什麼?其值除了可設定為 "session" 之外,還有哪些值可以設定?各有何不同?
參考文獻
[1] Web Developement with JavaServer Pages. Duane K. Fields, Mark A Kolb, Shawn Bayern. Manning, 2002.
[2] UML 精華第二版,Martin Fowler 著,趙光正、薛琇文 譯,基峰,2000。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.