Model-View-Controller Sample - 此範例以 MVC 架構實作了使用者登入和登出的功能。

Model-View-Controller 範例

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

地址: http://sun.cis.scu.edu.tw/~nms9115/articles/java/WebAppTutor/MVC/mvc.htm


1.0 簡介

這份文件包含了三個 MVC 範例,第一個範例是基於上一篇文章〔JSP、Servlet 與 JavaBean 的組合應用〕的基本架構,第二和第三個範例則是逐漸改良的版本。基本上,如果你已經了解上一篇文章的程式架構,這三個範例應該都很容易了解,所以這裡只會針對增加或改良的部分加以說明。

2.0 範例一

2.1 簡介

此範例以 MVC 架構實作了使用者登入和登出的功能。

2.2 檔案目錄結構

    classes  存放編譯過的 java class
      |
      +--com
          |
          +--huanlin

    src  存放所有原始碼檔案,包括 .java, .jsp, web.xml...等
      |
      +--Make.bat               用來編譯所有的 java 程式
      +--ControllerServlet.java 作為控制中心的 servlet
      +--UserInfoBean           用來儲存使用者資訊的 JavaBean
      +--Login.jsp              登入畫面
      +--Welcome.jsp            登入成功後的歡迎畫面

2.3 程式說明

在解讀這個範例程式時,最重要的,也是首先應該了解的,就是作為流程控制中心的 ControllerServlet 類別,它改寫了 HttpServlet 類別的 service() 方法,接收前端瀏覽器傳來的請求,處理之後傳回適當的 HTML 或 JSP 網頁。其過程如下:

  1. 取得前端 HTML 表單設定的 'action' 參數值。
  2. 判斷目前這個 session 的使用者是否已經登入,若否,則先進行身分驗證;若已登入,則根據 'action' 參數值判斷應將使用者導向到哪個網頁。

參考下列程式碼:

public class  extends HttpServlet {
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        
        response.setContentType("text/html; charset=big5");
        request.setCharacterEncoding("big5");

        String action = request.getParameter("action"); 

        
        if (!isAuthenticated(request) && !("authenticate".equals(action))) {
            doLogin(request, response);
            return;
        }
        if ("authenticate".equals(action)) {
            doAuthenticate(request, response);  
        }
        else if ("logout".equals(action)) {
            doLogout(request, response);        
        }
        else {
            response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
        }
    }

    
    private void (String targetURL, HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException {
        RequestDispatcher rd;
        rd = getServletContext().getRequestDispatcher("/" + targetURL);
        rd.forward(request, response);
    }

    
    private boolean (HttpServletRequest request) {
        boolean result = false;
        HttpSession session = request.getSession();
        if (session.getAttribute("userInfo") != null)
            result = true;
        return result;
    }

    
    private void (HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        gotoPage("Login.jsp", request, response);
    }

    
    private void (HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        HttpSession session = request.getSession();
        session.removeAttribute("userInfo");   
        session.invalidate(); 
        doLogin(request, response);             
    }

    
    private void (HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        String userName = request.getParameter("username");
        String password = request.getParameter("password");
        String targetURL;

        
        if ("123".equals(password)) {
            HttpSession session = request.getSession();

            UserInfoBean bean = new UserInfoBean();
            bean.setUserName(userName);
            bean.setPassword(password);

            session.setAttribute("userInfo", bean);
            targetURL = "/Welcome.jsp";
        }
        else {  
            targetURL = "/LoginError.jsp";
        }

        gotoPage(targetURL, request, response);
    }
}

提示:

  1. 注意 if ("123".equals(password)) 和 if (password.equals("123") 兩種寫法在防錯能力上的差異。
  2. 使用 RequestDispatcher.forward() 轉送網頁時,網址前面要加斜線 '/'。
  3. 前端的 'action' 參數定義,請參考 Login.jsp 和 Welcome.jsp。

2.4 編譯

執行 make.bat。

2.5 佈署與執行(for Tomcat)

  1. 在 Tomcat 的 webapps 目錄下建一個名為 mvc1 的目錄。
  2. 複製 web.xml 至 mvc1\WEB-INF\ 目錄下。
  3. 將所有 .jsp 檔案複製到 mvc1\ 目錄下。
  4. 將整個 classes 目錄複製到 mvc1\WEB-INF\ 目錄下。
  5. 重新啟動 Tomcat(只有第一次佈署時需要),在瀏覽器的網址列輸入 "http://127.0.0.1:8080/mvc1/main"。

參考以下執行畫面:

mvc1a.gif

mvc1b.gif

3.0 範例二

3.1 簡介

此版本改良自 MVC1,改變如下:

  • 以 Ant 來簡化編譯和佈署。
  • 原始碼的目錄結構區分得更清楚。
  • 將 ControllerServlet 類別中,處理 action 參數與相對應的動作抽離成一個框架,將固定不變的留下來,經常會變動的部分移出去。也就是把 action 定義成抽象類別,實際的具像類別由個別的類別單獨實作,並且將 action 參數與相對應的 action 具像類別定義在一個外部的屬性檔裡面。這個屬性檔就是 action.properties。

3.2 檔案目錄結構

    cfg  存放應用程式組態檔

    classes  存放編譯過的 java class
      |
      +--com
          |
          +--huanlin
               |
               +--action
               +--servlet
               +--utility
               +--valuebean

    jsp  存放 .jsp 檔案
      |
      +--Login.jsp              登入畫面
      +--LoginError.jsp         登入失敗畫面
      +--Welcome.jsp            登入成功後的歡迎畫面

    src  存放 java 原始碼檔案
      |
      +--action.properties  定義 action 與其對應的類別
      +--com
          |
          +--huanlin
               |
               +--action  處理 HTTP request 的 Action 具像類別
                    |
                    +--AuthenticationAction.java 驗證使用者身分
                    +--EmployeeAction.java       員工資料維護作業
                    +--LogoutAction.java         登出

               +--servlet
                    |
                    +--ControllerServlet.java    作為控制中心的 servlet

               +--utility
                    |
                    +--Action   Action 物件的抽象類別

               +--valuebean
                    |
                    +-- UserInfoBean    用來儲存使用者資訊的 JavaBean
Action 族系的類別階層
    java.lang.Object
      |
      +--com.huanlin.utility.Action
           |
           +--com.huanlin.action.AuthenticationAction
           +--com.huanlin.action.EmployeeAction
           +--com.huanlin.action.LogoutAction
其他類別
    java.lang.Object
      |
      +--com.huanlin.servlet.ControllerServlet
      +--com.huanlin.valuebean.EmployeeBean
      +--com.huanlin.valuebean.UeerInfoBean

3.3 程式說明

ControllerServlet 類別中,已經不像範例一裡面,使用許多 if 敘述來判斷要執行哪個 action。這次使用了物件導向的多型技術,將 action 的執行動作定義在一個抽象類別 Action 裡面,所有實際要執行的動作都必須繼承自 Action 類別。同時,我們也使用了「以類別名稱建立物件個體」的程式技巧,讓我們可以將 action 參數與其對應的 Action 類別定義在一個外部檔案,程式執行時才讀入,並且根據類別名稱(字串)來建立物件。過程如下:

  1. Servlet 初始化時,亦即 init() 方法,先從 action.properties 檔案中讀入參數與動作類別的對應表,並將此對應表儲存在一個名為 'actions' 的 hashtable 裡面。
  2. Servlet 收到 request 時,亦即 service() 方法,先判斷使用者是否已經登入,若否,則先進行身分驗證;若已登入,則根據 'action' 參數值,到 hashtable 裡面取得對應的類別名稱,再由類別名稱動態建立物件個體。Action 物件建立好之後,呼叫 execute() 方法。
package com.huanlin.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import com.huanlin.valuebean.*;
import com.huanlin.utility.*;

public class ControllerServlet extends HttpServlet {
    
    private static ResourceBundle actionRB = ResourceBundle.getBundle("action");
    private static Hashtable actions = new Hashtable();

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        initCommandMapping();
    }

    
    private void () {
        Enumeration enum = actionRB.getKeys();
        while (enum.hasMoreElements()) {
            String s = (String) enum.nextElement();
            actions.put(s, actionRB.getObject(s));
        }
    }

    public void (HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        
        response.setContentType("text/html; charset=big5");
        request.setCharacterEncoding("big5");

        String actionName = request.getParameter("action"); 

        
        if (!isAuthenticated(request) && !("authenticate".equals(actionName))) {
            doLogin(request, response);
            return;
        }

        String className = (String) actions.get(actionName);
        if (className == null) {
            doError(request, response);
            return;
        }

        // 動態建立 action 物件。
        try {
            Class classObject = Class.forName(className);
            Action action = (Action) classObject.newInstance();
            String targetURL = action.execute(this, request, response);
            if (targetURL != null)
                gotoPage(targetURL, request, response);
        }
        catch (Exception e) {
            throw new ServletException(e);
        }
    }

    
}

提示:

  1. 注意 ResourceBundle 類別的用法,以及 action.properties 檔案存放的位置。
  2. 看一下 action.properties 檔案的內容,了解 action 和類別的對應關係如何定義。
  3. 查閱 Hashtable 類別的用法。
  4. 查閱 Java API 關於 Class.forName() 和 Class.newInstance() 的說明。
  5. 看一下 action.java 中如何定義 Action 抽象類別,以及其他繼承的類別如何實作。

3.4 編譯、佈署、與執行(for Tomcat)

此範例使用 Ant 來協助應用程式的編譯和佈署,因此你必須先安裝 Ant,安裝步驟可以參考 5.0 一節的說明。

安裝好之後,只要在此應用程式的目錄下輸入下列 DOS 命令即可:

ant

Ant 會讀取 build.xml 檔案的內容來執行命令,我們已經是先將所有檔案的編譯和佈署命令都寫在 build.xml 檔案裡面,所以編譯和佈署都是全自動化的。

執行時在瀏覽器的網址列輸入:"http://127.0.0.1:8080/mvc2/main" 即可。執行畫面與範例一雷同。

4.0 範例三

4.1 簡介

此版本改良自 MVC2,改變如下:

  • 加入資料庫的存取(MSSQL 2000)。
  • 增加 DbHelper 類別,用來建立資料庫的連結。
  • 修改 Action 介面,增加 ActionBase 基礎類別。
  • 增加 AppConstants 類別,用來存取定義於外部檔案的應用程式參數,此範例是將資料庫連線參數寫在外部檔案 'app.properties' 裡面。

4.2 檔案目錄結構

    cfg  存放應用程式組態檔

    classes  存放編譯過的 java class
      |
      +--com
          |
          +--huanlin
               |
               +--action
               +--servlet
               +--utility
               +--valuebean

    jsp  存放 .jsp 檔案
      |
      +--Login.jsp              登入畫面
      +--LoginError.jsp         登入失敗畫面
      +--Welcome.jsp            登入成功後的歡迎畫面

    src  存放 java 原始碼檔案
      |
      +--com
          |
          +--huanlin
               |
               +--action  Action 具像類別
                    |
                    +--AuthenticationAction 驗證使用者身分
                    +--EmployeeAction       員工資料維護作業
                    +--LogoutAction         登出

               +--servlet
                    |
                    +--ControllerServlet  作為控制中心的 servlet

               +--utility
                    |
                    +--Action        用來處理 HTTP request 的 Action 物件的介面
                    +--ActionBase    Action 基礎類別
                    +--AppConstants  用來存取應用程式常數的類別
                    +--DbHelper      此類別包含用來協助處理資料庫的方法

               +--valuebean
                    |
                    +-- EmployeeBean  代表一名員工
                    +-- UserInfoBean  儲存使用者資訊
Action 族系的類別階層
    java.lang.Object
      |
      +--com.huanlin.utility.ActionBase
           |
           +--com.huanlin.action.AuthenticationAction
           +--com.huanlin.action.EmployeeAction
           +--com.huanlin.action.LogoutAction
其他類別
    java.lang.Object
      |
      +--com.huanlin.servlet.ControllerServlet
      +--com.huanlin.utility.AppConstants
      +--com.huanlin.utility.DbHelper
      +--com.huanlin.valuebean.EmployeeBean
      +--com.huanlin.valuebean.UeerInfoBean

4.3 程式說明

這個範例跟前一個版本主要的差異在於加入了處理資料庫的能力,因此 Action 抽象類別也由原先的一個方法,增加為四個方法,並且改成以 interface 來定義,如下:

package com.huanlin.utility;
import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;

public interface Action {

    
    public void setConnection(Connection con);

    
    public boolean execute(HttpServlet servlet,
            HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException;

    
    public String getView();

    
    public Object getModel();
}

其中

  • setConnection() 是用來指定資料庫連線物件,換句話說,外界(即 ControllerServlet)必須先建立好資料庫連線物件,並且在執行時傳入。
  • execute() 執行這個 action 所需的處理。傳回值改為布林型態,False 表示執行失敗,True 表示執行成功。
  • getView() 會傳回一個網址。ControllerServlet 可使用此方法得知要轉往哪個頁面。
  • getModel() 回傳回一個資料物件。ControllerServlet 可使用此方法取得資料物件,並傳遞給 JSP 程式。

從介面的各個名稱可以看出,這是個名副其實的 MVC 架構。為了簡化實作類別,我們再定義一個 ActionBase 基礎類別,此類別實作了 Action 介面,並且把常用的方法實作出來,這樣後代在繼承時就不用寫重複的程式碼了。

ControllerServlet 類別必須在初始化時就建立好資料庫連線,並且在呼叫每個 action 物件的 execute() 方法之前將資料庫連線物件傳遞給它。ControllerServlet 的原始碼如下:

package com.huanlin.servlet;
import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import com.huanlin.valuebean.*;
import com.huanlin.utility.*;

public class ControllerServlet extends HttpServlet {
    
    private Connection dbCon = null;

    
    private static ResourceBundle actionRB = ResourceBundle.getBundle("action");
    private static Hashtable actions = new Hashtable();

    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        
        System.setProperty("prop.file.dir",
            getServletContext().getRealPath("") + "\\WEB-INF\\");

        try {
            dbCon = DbHelper.getConnection();
        }
        catch (ClassNotFoundException e) {
            
            System.out.println("無效的資料庫驅動程式!\n" + e);
        }
        catch (SQLException e) {
            
            System.out.println("無法載入資料庫驅動程式!\n" + e);
        }

        initCommandMapping();
    }

    public void destroy()
    {
        try {
            dbCon.close();
        }
        catch (java.sql.SQLException e) {
            
        }
    }

    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        
        response.setContentType("text/html; charset=big5");
        request.setCharacterEncoding("big5");

        String actionName = request.getParameter("action"); 

        
        if (!isAuthenticated(request) && !("authenticate".equals(actionName))) {
            doLogin(request, response);
            return;
        }

        String className = (String) actions.get(actionName);
        if (className == null) {
            doError(request, response);
            return;
        }

        
        try {
            
            Class classObject = Class.forName(className);
            Action action = (Action) classObject.newInstance();

            
            action.setConnection(dbCon);

            if (action.execute(this, request, response)) {

                
                String targetURL = action.getView();

                
                request.setAttribute("model", action.getModel());

                if ((targetURL != null) && (targetURL != "")) {
                    gotoPage(targetURL, request, response);
                }
            }
        }
        catch (Exception e) {
            throw new ServletException(e);
        }
    }

    
}

在 init() 方法中,使用了 DBHelper 類別來建立資料庫連線,而 DbHelper 則借助 AppConstants 類別將連線參數從外部檔案 app.properties 讀入,這部分請自行閱讀原始碼以了解相關細節。如果你不想要了解實作細節,DbHelper 和 AppConstants 也可以直接拿來重複使用,只要你知道 app.properties 在佈署時應放在哪裡,以及資料庫連線參數如何定義就行了。

4.4 編譯、佈署、與執行(for Tomcat)

跟範例二一樣使用 Ant 編譯,在此應用程式的目錄下輸入下列 DOS 命令即可完成編譯和佈署:

ant

執行時在瀏覽器的網址列輸入:"http://127.0.0.1:8080/mvc3/main" 即可。

5.0 附錄:Ant 安裝指南

5.1 Ant 簡介

Ant 是 Apache 開放原始碼專案的其中一項,它是個 Java 應用程式的輔助建立工具。透過 Ant,我們可以將日常的編譯及佈署 Java 應用程式的動作全部自動化,其功能類似 make,但是更強大,而且具備跨平台的優點。

5.2 取得 Ant

http://ant.apache.org/bindownload.cgi 下載最新版本的 Ant。如果你不需要重新編譯 Ant,只要下載 binary distribution 就好了,source code 可以不用下載。

5.3 安裝 Ant (for Windows)

你的系統必須已經安裝好 JDK,才能安裝 Ant。Windows 平台的安裝步驟如下:

  1. 為 Ant 建立一個目錄,將下載下來的 Ant 壓縮檔解開至這個目錄,假設是 'C:\Ant\'。
  2. 新增一個系統環境變數 'ANT_HOME',其值為 'C:\Ant'。
  3. 將 '%ANT_HOME%\bin' 加入 PATH 環境變數中。

下載所有範例程式

转载于:https://www.cnblogs.com/kaixin110/archive/2007/10/07/916191.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值