迷你MVC教程

迷你MVC教程——命令行方式創建第一個Servelt小程序

本文節選自《Head First Servlets and JSP》第3章 MVC實戰,根據個人配置和實操情況有所刪改。本人注解部分用斜體表示。圖片截取自原書,部分為個人操作截圖。本人電腦配置win10x64, tomcat7, jdk10。本教程的特色處在于全程采用手工編寫和命令行結合,不借助IDE開發環境,適合初次接觸Servlet者當作練習的小項目。任何問題請在下方留言。

構建小型Web應用步驟

  1. 分析用戶的視圖以及高層體系結構
  2. 創建用于開發這個項目的開發環境
  3. 創建用于部署這個項目的部署環境
  4. 對Web應用的各個組件完成迭代式的開發和測試

(基于Beer Advisor的案例。根據用戶選擇的酒色提供酒的品牌建議的servlet小程序

用戶視圖

  • 用戶最初請求的HTML表單,生成HTTP POST請求
  • 處理請求返回的result.jsp

體系結構

  1. 客戶請求得到form.html頁面
  2. 容器獲得form.html頁面
  3. 容器把這個頁面返回給瀏覽器,用戶再在瀏覽器上回答表單上的問題
  4. 瀏覽器把請求數據發送給容器
  5. 容器根據URL查找正確的servlet,并把請求傳遞給這個servlet
  6. servlet調用BeerExpert尋求幫助
  7. BeerExpert類返回一個回答,servlet把這個回答增加到請求對象
  8. servlet把請求轉發給JSP
  9. JSP從請求對象得到回答
  10. JSP為容器生成一個頁面
  11. 容器將jsp頁面返回給用戶
體系結構

開發環境

IDE項目的目錄結構

開發環境

部署環境

將Web項目部署到容器中

部署環境

迭代式的開發和測試

  1. 構建和測試用戶最初請求的HTML表單
  2. 構建控制器servlet的第一個版本,并用HTML表單測試這個控制器。這個版本通過HTML表單來調用,并打印出它接收到的參數
  3. 為模型類構建一個測試類,然后構建并測試模型類本身
  4. 把servlet升級到第2版。這個版本增加了一個功能,可以調用模型類來得到啤酒建議。
  5. 構建JSP, 把servlet升級到第3版本(增加一個功能,可以把表示流分派到JSP完成),然后再測試整個應用。

第一個表單頁面的HTML

form.html包含標題文本,一個下拉列表,還有一個提交按鈕(原書代碼)

<html>
    <body>
        <h1 align="center">
            Beer Selection Page
        </h1>
        <!--為什么選擇POST而不是GET? HTML認為這就是要調用servlet。在你的目錄結構里沒有一個叫“SelectBeer.do”的東西。這只是一個邏輯名-->
        <form method="POST" action="SelectBeer.do">
            <p>
                Select beer characteristics
            </p>
            Color:
            <select name="color" size="1">
                <!--我們就是這樣拆個那就下拉菜單的,你可以有自己不同的選項-->
                <option value="light">light</option>
                <option value="amber">amber</option>
                <option value="brown">brown</option>
                <option value="dark">dark</option>
            </select>
            <br><br>
            <center>
            <input type="SUBMIT">
            </center>
        </form>
    </body>
</html>

部署和測試開始頁面

  1. 在開發環境中創建HTML

創建這個HTML文件,取名為form.html,然后保存在開發環境的/beerV1/web/目錄下

  1. 把這個文件復制到部署環境

把form.html文件的一個副本放在tomcat/webapps/Beer-vl/中

  1. 在開發環境中創建DD

創建XML文檔,取名為web.xml, 把它保存在開發環境的/beer/etc/目錄下(原書代碼)

<!--沒有必要知道這是什么意思,只需要照著輸入就行-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

  <servlet>
      <!--這是一個虛構的名字,只能在DD的其他部分使用-->
    <servlet-name>Ch3 Beer</servlet-name>
      <!--servlet類文件的完全限定名-->
    <servlet-class>com.example.web.BeerSelect</servlet-class>
  </servlet>
    
  <servlet-mapping>
    <servlet-name>Ch3 Beer</servlet-name>
      <!--不要忘記最前面有一個斜線。我們希望用戶這樣引用“servlet.do”只是一個約定-->
    <url-pattern>/SelectBeer.do</url-pattern>
  </servlet-mapping>
    
</web-app> 

由于原書代碼是tomcat5, 版本有所變化,可以到tomcat/webapps/ROOT/WEB-INF自帶目錄下拷貝了一份web-xml文件,并添加相應的映射語句。

  1. 把這個文件復制到部署環境

把web.xml文件的一個副本放在tomcat/webapps/Beer-v1/WEB-INF/

  1. 啟動Tomcat

Tomcat既作為Web服務器,又作為Web容器。要啟動Tomcat, 先用cd命令切換到tomcat主目錄,再運行bin/startup.sh

啟動Tomcat

命令行中用cd切換到tomcat/bin目錄下(tomcat根據版本名稱不同),win系統運行startup.bat

  1. 測試頁面

在瀏覽器中打開這個HTML頁面,為此鍵入:

http://localhost:8080/Beer-v1/form.html

邏輯名映射到servlet類文件

以下部分為xml文件中配置的詳解

  1. 用戶填寫表單,然后點擊submit。瀏覽器生成了以下請求URL:

/[1]Beer-v1[2]/SelectBeer.do[3]

在用戶發送的http post請求中,“/Beer-v1”不是路徑的一部分。在form.html中,它只說:<form method="POST" action="SelectBeer.do>"

但瀏覽器為請求追加了“/Beer-v1/”,因為用戶請求就來自這里。換句話說,form.html中的“SelectBeer.do”相對于其所在頁面的URL。在這里,就是相對于Web應用的根:“/Beer-v1”

  1. 容器搜索DD, 找到<url-pattern>與/SelectBeer.do匹配的一個<servlet-mapping>, 這里的斜線{/}表示Web應用的上下文根,SelectBeer.do就是資源的邏輯名
  2. 容器看到對應這個<url-pattern><servlet-name>是“Ch3 Beer”。但是這并不是實際servlet類文件的名字。“Ch3 Beer”是servlet名,而不是servlet類的名字。

對容器來說,servlet只是在DD中<servlet>標記下的一個東西。servlet名只在DD中使用,以便DD的其他部分建立與該servlet的映射

  1. 容器查找<servlet-name>為“Ch3 Beer”的<servlet>標記
  2. 根據<servlet>標記中的<servlet-class>,容器可以知道有哪個servlet類負責處理這個請求。如果這個servlet還沒有初始化,就會加載類,并初始化servlet
  3. 容器開始一個新線程來處理這個請求,并把請求傳遞給這個線程(傳遞給servelt的service()方法)
  4. 容器把響應(通過Web服務器)發回給用戶

控制器servlet的第1版

確保HTML頁面能適當地調用servlet, 而且servlet能正確地接收HTML參數。

(原書代碼)

//確保與前面創建的開發結構和部署結構匹配
package com.example.web;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

//HttpServlet擴展了GenericServlet,GenericServlet則實現了Servlet接口
public class BeerSelect extends HttpServlet{
    
    //我們使用doPOST來處理HTTP請求,因為HTML表單指出,method=POST
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException{
        
        //這個方法來自ServletResponse接口
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("Beer Selection Advice<br>");
        //這個方法來自ServletRequest接口。注意這個參數與HTML<select>標記中"name"屬性的值匹配
        String c = request.getParameter("color");
        //這里我們沒有返回建議,只是把測試信息顯示出來
        out.println("<br>Got beer color "+c);
    }
}

編譯、部署和測試控制器servlet

編譯servlet

用-d標志編譯servlet,把類放在開發環境中

原書

編譯servlet

win系統

win系統編譯servelt

注解:-classpath用于設置臨時環境變量,指定查找用戶類文件和注釋處理程序的位置;本例中由于編譯servlet需要用到額外類庫,tomcat提供了這些jar包,故選定tomcat/lib/servlet-api.jar為環境變量。書中還添加了classes和"."(代表當前目錄)作為路徑,其實可以略去。

-d用于指定編譯生成的class文件存放目錄,本例中存放于classes路徑中,由于servlet的package語句,會自動生成com.example.web目錄(若不存在)。

最后,添上servlet類的存放路徑,若存放在當前目錄則直接輸入文件名。

部署servlet

要部署servlet, 建立.class文件的一個副本,并把它移到部署結構的/Beer-v1/WEB-INF/classes/com/example/web/目錄下

測試

  1. 重啟tomcat
重啟tomcat

直接在tomcat/bin目錄下輸入startup.bat命令即可重啟,無須關閉

  1. 啟動瀏覽器,訪問:

http://localhost:8080/Beer-v1/form.html

  1. 選擇一種啤酒顏色,點擊“Submit”
  2. 如果你的servlet能正常運行,就能在瀏覽器上看到servlet的響應顯示為:

Beer Selection Advice

Got beer color brown

構建和測試模型類

模型規范

  • 包應當是com.example.model
  • 其目錄結構應當是/WEB-INF/classes/com/model
  • 提供一個方法getBrands(), 取一個喜歡的啤酒顏色(String)作為參數,并返回一個ArrayList, 其中包含推薦的啤酒品牌(String)

為模型構建測試類

為模型創建測試類(在構建模型本身之前先創建測試類)。剛開始測試模型時,模型還在開發環境中,與其他Java類一樣,此時無需啟動Tomcat也能測試

作者代碼(我是在創建模型類后再創建測試類)

package com.example.model;

import java.util.*;

class BeerExpertTest{
    public static void main(String[] args){
    BeerExpert be=new BeerExpert();
    List<String> testBrands1=be.getBrands("amber");
    Iterator it1=testBrands1.iterator();
    while(it1.hasNext()){
    System.out.println("try1: "+it1.next());
        }
    System.out.println("----------------------");
    List<String> testBrands2=be.getBrands("");
    Iterator it2=testBrands2.iterator();
    while(it2.hasNext()){
    System.out.println("try2: "+it2.next());
        }
    }
}

命令行中運行測試類(請在構建模型類后進行此步)

編譯測試類

運行此步后,開發環境項目中的/classes/com/example/model中會生成兩個.class文件(原來BeerExpert編譯的.class文件被更新)。-classpath中的./src為測試類所依賴的模型類(com.example.model.BeerExpert)的查找路徑。原教材中無此步,可略過。

可以切換至測試類所在包的基目錄,然后運行java命令

解析運行測試類方法1

也可以直接在beerV1/下設置環境變量為classes目錄

解析運行測設類方法2

構建和測試模型

(原書代碼,增加了List泛型為String類)

package com.example.model;
import java.util.*;

public class BeerExpert{
    public List<String> getBrands(String color){
        List<String> brands = new ArrayList()<String>;
        if (color.equals("amber")){
            brands.add("Jack Amber");
            brands.add("Red Moose");
        }else{
            brands.add("Jail Pale Ale");
            brands.add("Gout Stout");
        }
        return(brands);
    }
} 
編譯模型類

win系統與上圖操作命令一致,因為模型類中沒有導入其他外部類,所以無須設置classpath變量

改進servlet,調用模型得到真正的建議

第2版的servlet中,通過改進doPost()方法,調用模型來得到建議(第3版還會向JSP提供建議)

改進servlet, 第2版

先把servlet放在一邊,只考慮Java

  1. 改進doPost()方法來調用模型
  2. 編譯servlet
  3. 部署和測試更新后的Web應用

(作者代碼,增加了List泛型部分,作者改用增強for循環遍歷集合,也可采用原書中的iterator迭代器遍歷)

import com.example.model;
public class BeerSelect2 extends HttpServlet{
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("Beer selection Advice<br>");
        String c = request.getParameter("color");
        BeerExpert be = new BeerExpert();
        List<String> advisedBrands= be.getBrands(c);
        for(String ad:advisedBrands){
            out.print("<br>try:"+ad);
        }
    }
}

注:原書中每次修改都替換原始的servlet, 作者采用創建新的servlet方式,以便區分不同版本的servlet——此方式需要在xml配置文件中相應修改servlet類完全限定名

第2版Servlet代碼

package com.example.web;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import com.example.model.*;
import java.util.*;

public class BeerSelect2 extends HttpServlet{
    
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException{
             
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("Beer selection Advice<br>");
        String c = request.getParameter("color");
        BeerExpert be = new BeerExpert();
        List<String> advisedBrands= be.getBrands(c);
        for(String ad:advisedBrands){
            out.print("<br>try:"+ad);

    }
}
}

第2版servlet的關鍵步驟

主要有兩件事要做:重新編譯servlet和部署模型類

編譯servlet

編譯servlet

win系統下

win系統編譯servlet

注:第2版servlet與前一版不同之處在于調用了模型,故在classpath環境變量中添加模型類的路徑

部署和測試Web應用

  1. 把servlet.class文件的一個副本移到以下位置:

../Beer-v1/WEB-INF/classes/com/example/web/

這會替換第1版的servlet類文件

  1. 把模型的.class文件副本移到:

../Beer-v1/WEB-INF/classes/com/example/model/

  1. 關閉并重啟tomcat
  2. 通過form.html測試這個應用

創建提供建議的JSP”視圖”

<!--這是一個“頁面指令”-->
<%@ page import="java.util.*" %>

<html>
    <body>
        <!--一些標準HTML模板-->
        <h1 align="center">
            Beer Recommendations JSP
        </h1>
        <br>
        <!--以下稱為scriptlet代碼,用于輸入java代碼-->
        <%
        //這里從請求對象得到一個屬性
        List styles=(List)request.getAttribute("styles");
        Iterator it = styles.iterator();
        while(it.hasNext()){
            out.print("<br>try:"+it.next());
        }
        %>
    </body>
</html>

部署JSP

不用編譯JSP(這個工作會在第一個請求到達容器時由容器完成)

  1. 把它命名為“result.jsp"
  2. 保存再開發環境的/web/中
  3. 將其副本移到部署環境的/Beer-v1/中

改進這個servlet,讓它”調用“JSP(第3版)

這一步,我們要把servlet修改為”調用“JSP來生成輸出(視圖)。容器提供了一種稱為”請求分派“的機制,允許容器管理一個組件調用另一個組件。我們通過使用這種機制,servlet從模型中得到信息,把它保存在請求對象中,然后把請求分派給JSP

必須對這個servlet做的重要修改

  1. 把模型組件的回答增加到請求對象,以便JSP訪問
  2. 要求容器把請求轉發給”result.jsp"

第3版servlet的代碼

如下修改servlet, 將模型組件的回答增加到請求對象(以便JSP獲取),并要求容器把請求分派給JSP

(作者代碼,修改了類名和泛型,需要在xml文件修改配置)

package com.example.web;

import com.example.model.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class BeerSelect3 extends HttpServlet{
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{
        String c = request.getParameter("color");
        BeerExpert be = new BeerExpert();
        List<String> result = be.getBrands(c);
        
       //為請求對象增加一個屬性,供JSP使用。注意,JSP要尋找"styles"
        request.setAttribute("styles", result);
        //為JSP實例化一個請求分派器
        RequestDispatcher view = request.getRequestDispatcher("result.jsp");
        //使用請求分派器要求容器準備好JSP,并向JSP發送請求和響應
        view.forward(request,response);
    }
}

編譯、部署和測試最后的應用

編譯servlet

部署和測試Web應用

  1. 把servlet的.class文件副本移到../Beer-v1/WEB-INF/classes/com/example/web/
  2. 關閉并重啟tomcat
  3. 通過form.html測試應用

  1. 主機服務器的根 ?

  2. Web應用上下文 ?

  3. 邏輯資源名 ?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。