JAVA WEB SERVLET 基礎知識點

Servlet


掌握Servlet API 是成爲一名技術高超的JAVA Web開發者的基礎.必須非常熟悉Servlet API中所定義的70多種類型.
本篇博客將介紹Servlet API,並教你編寫第一個Servlet應用程序.

1.Servlet API 概述

Servlet API中有4個Java包,包括:

  • javax.servlet. 包含定義Servlet與Servlet容器之間契約的類和接口.
  • javax.servlet.http. 包含定義HTTP Servlet與Servlet容器之間契約的類和接口.
  • javax.servlet.annotation. 包含對Servlet Filter和Listener進行標註的註解.它還爲標註元件指定元數據.
  • javax.servlet.descriptor. 包含爲Web應用程序的配置信息提供編程式訪問的類型.

Servlet技術的核心是Servlet接口,這是所有Servlet類都必須直接或者間接實現的一個接口.Servlet接口定義了Servlet與Servlet容器之間的一個契約,Servlet容器會把Servlet類加載到內存中,並在Servlet實例中調用特定的方法.每個Servlet類型只能有一個實例.

Servlet容器還爲每個應用程序創建一個ServletContext實例.這個對象封裝context(應用程序)的環境細節.每個context只有一個ServletContext.每個Servlet實例還有一個封裝Servlet配置信息的ServletConfig.

2.Servlet

Servlet接口中定義了以下5個方法:

public void init(ServletConfig servletConfig)throws ServletException 

public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException 

public void destroy()

public ServletConfig getServletConfig() 

public String getServletInfo() 

編寫Java方法簽名的規則是:與包含該方法的類型不在同一個包中的類型,要使用全類名.
init() service() destroy()方法是Servlet的生命週期方法,調用規則如下:

  • init(): 第一次請求Servlet時,Servlet容器會調用這個方法.在後續的請求中不會在調用.
  • service(): 每次請求Servlet時都會調用此方法,必須在這裏編寫要Servlet完成的響應代碼. 第一次調用Servlet時會調用init()和service()方法,之後請求只調用service()方法.
  • destroy(): Servlet要銷燬時調用此方法,通常發生在卸載應用程序,或者關閉Servlet容器的時候.
  • getServletInfo(): 該方法返回Servlet的描述,可以是任意字符串,甚至爲null.
  • getServletConfig(): 該方法返回由Servlet容器傳給init方法的ServletConfig.爲了讓getServletConfig返回非null值,應該爲傳給init方法的ServletConfig賦給一個類級變量.

必須注意線程安全.一個應用程序中的所有用戶將共用一個Servlet實例,因此 不建議使用類級變量,除非它們是隻讀的,或者是java.util.concurrent.atomic包中的成員.

3.編寫基礎的Servlet應用程序

Servlet應用程序編寫起來非常簡單,只需要創建一個目錄結果,並將Servlet類放在某一個目錄下即可.

3.1編寫和編譯Servlet類

在本節中,將學習如何編寫一個簡單的Servlet應用程序,將它命名爲app01a.最初它只包含一個Servlet:MyServlet,其會給用戶發送一條問候信息.

package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "MyServlet", urlPatterns = { "/my" })
public class MyServlet implements Servlet {

    private transient ServletConfig servletConfig;

    @Override
    public void init(ServletConfig servletConfig)
            throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public String getServletInfo() {
        return "My Servlet";
    }

    @Override
    public void service(ServletRequest request,
            ServletResponse response) throws ServletException,
            IOException {
        String servletName = servletConfig.getServletName();
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head>"
                + "<body>Hello from " + servletName 
                + "</body></html>");
    }

    @Override
    public void destroy() {
    }    
}

查看代碼首先會注意到下面這個註解:

@WebServlet(name = “MyServlet”, urlPatterns = { “/my” })
註解中:name屬性是可選的,一般用來提供Servlet類的名字,關鍵是urlPatterns它也是可選的,在MyServlet中,urlPatterns告訴容器,/my模式應該調用這個Servlet.

  • URL模式必須一條正斜線開頭.

3.2應用程序的目錄結構

Servlet應用程序必須以特定目錄結果進行部署,應用程序包含WEB-INF目錄
它有兩個子目錄:

  • classes目錄:Servlet類和其他的Java類都必須放在這裏.類下方的目錄反映了類的包結構.
  • lib目錄:在這裏部署Servlet應用程序所需要的Jar文件.
    所有的圖像文件可以放在一個image目錄下,所有的JSP頁面可以放在一個jsp目錄下,以此類推.
    放在應用程序目錄下的任何資源,用戶可以通過輸入該資源的URL而直接進行訪問.如果希望某個資源可以被Servlet訪問,但是不能被用戶訪問,那麼應該把它放在WEB-INF目錄下面.

把應用程序部署到Tomcat中:

  • 一種部署方法是將應用程序目錄直接複製到Tomcate的webapps目錄下
  • 可以通過在Tomcat的conf目錄下編輯server.xml文件來部署應用程序
  • 爲了不用編輯server.xml,而單獨部署一個XML文件到conf\Catalina\localhost目錄下.
  • 將應用程序打包成war文件來進行部署.war文件是指以war作爲擴展名的jar文件.

3.3調用Servlet

運行MyServlet應用程序,通過以下地址:

http://localhost:8080/app01a/my

允許結果:

恭喜!你的第一個Servlet應用程序已經運行!

4.ServletRequest

下面是ServletRequest接口中的部分方法:

public int getContentLength()

返回請求主體中的字節數,如果不知道字節長度,則返回-1;

public java.lang.String getContentType()

返回請求主體的MIME類型,如果不知道類型,則返回null;

public java.lang.String getProtocol()

返回這個HTTP請求的協議名稱和版本號;

public java.lang.String getParameter(java.lang.String name)

返回指定請求參數的值,返回一個HTML表單域的值,獲取查詢字符串的值.如果不存在返回null;

利用getParameterNames getParameterMap和getParameterValues來獲取表單域的名稱和值,以及查詢字符串.

5.ServletResponse

  • ServletResponse中定義了getWriter()方法,它返回可以將文本傳給客戶端的java.io.PrintWriter.默認情況下,PrintWriter對象採用ISO-8859-1編碼.

  • 在發送任何HTML標籤之前,先通過調用setContentType來設置響應的內容類型,比如:將text/html作爲參數傳遞,告訴瀏覽器內容類型爲HTML.

6.ServletConfig

  • getInitParameter(java.lang.String name)

從一個Servlet內部獲取某個初始參數的值,由傳入init方法的ServletConfig中調用getInitParameter.

  • java.util.Enumeration getInitParameterNames()

返回所有初始參數名稱的一個Enumeration.

  • getServletContext()

從Servlet內部獲取ServletContext.

舉個ServletConfig的例子.添加一個ServletConfigDemoServlet:

package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "ServletConfigDemoServlet", 
    urlPatterns = { "/servletConfigDemo" },
    initParams = {
        @WebInitParam(name="admin", value="Harry Taciak"),
        @WebInitParam(name="email", value="[email protected]")
    }
)
public class ServletConfigDemoServlet implements Servlet {
    private transient ServletConfig servletConfig;

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public void init(ServletConfig servletConfig) 
            throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    public void service(ServletRequest request, 
            ServletResponse response)
            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head><body>" + 
                "Admin:" + admin + 
                "<br/>Email:" + email +
                "</body></html>");
    }

    @Override
    public String getServletInfo() {
        return "ServletConfig demo";
    }

    @Override
    public void destroy() {
    }    
}

7.ServletContext

ServletContext表示Servlet應用程序.每個Web應用程序只有一個context.
用ServletConfig中調用getServletContext方法可以獲得ServletContext.
保存在ServletContext中的對象稱作屬性.
ServletContext中包含如下常用屬性:
- getAttribute()
- getAttributeNames()
- setAttribute()
- removeAttribute()

8.GenericServlet

通過Servlet接口編寫Servlet很麻煩,需要把它的所有方法都實現及時不用的方法,所以,我們有GenericServlet抽象類,它實現了Servlet和ServletConfig,並做了以下工作:

  • 將init方法中的ServletConfig賦給一個類級變量,使它可以通過調用getServletConfig來獲取.
  • 爲Servlet接口中的所有方法提供默認實現.
  • 提供方法來包裝ServletConfig中的方法.

在類中如果覆蓋了init()方法,則必須調用super.init(servletConfig)來保存ServletConfig.
可以通過覆蓋無參的init方法來編寫初始化代碼,ServletConfig仍然由GenericServlet實例保存.
接下來舉個繼承GenericServlet的Servlet栗子:

package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "GenericServletDemoServlet", 
    urlPatterns = { "/generic" },
    initParams = {
        @WebInitParam(name="admin", value="Harry Taciak"),
        @WebInitParam(name="email", value="[email protected]")
    }
)
public class GenericServletDemoServlet extends GenericServlet {

    private static final long serialVersionUID = 62500890L;

    @Override
    public void service(ServletRequest request, 
            ServletResponse response)
            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head><body>" + 
                "Admin:" + admin + 
                "<br/>Email:" + email +
                "</body></html>");
    }
}

9.1 HTTPServlet

javax.servlet.http包是Servlet API中的第二個包,其包含了編寫Servlet應用程序的類和接口.
HttpServlet類覆蓋javax.servlet.GenericServlet類.
HttpServlet中有兩項特性是GenericServlet所沒有的:

  • 不覆蓋service方法,而是覆蓋doGet doPost,或者兩者都覆蓋掉.在極少數情況下,還要覆蓋以下某個方法:doHead() doPut() doTrace() doOptions()或doDelete().
  • 將用HttpServletRequest和HttpServletResponse代替ServletRequest和ServletResponse.

9.2 HttpServletRequest

HttpServletRequest表示HTTP環境中的Servlet請求.它繼承java.servlet.ServletRequest接口,並增加了幾個方法:

  • String getContextPath()

返回表示請求context的請求URI部分.

  • Cookie[] getCookies()

返回一個Cookie對象數組.

  • String getHeader(String name)

返回指定HTTP標頭的值.

  • String getMethod()

返回發出這條請求的HTTP方法的名稱.

  • String getQueryString()

返回請求URL中的查詢字符串.

  • HttpSession getSession()

返回與這個請求有關的session對象.如果沒有找到,則創建新的session對象.

  • HttpSession getSession(boolean create)

返回與這個請求有關的session對象.如果沒有找到,並且create參數爲true,那麼將創建新的session對象.

9.3 HttpServletResponse

HttpServletResponse表示HTTP環境下的Servlet響應.

  • void addCookie(Cookie cookie) 給這個響應對象添加cookie
  • void addHeader(String name,String value) 給這個響應對象添加標頭.
  • void sendRedirect(String location) 發送響應代號,將瀏覽器重定向到指定的位置.

10.處理HTML表單

每個Web應用程序中幾乎都會包含一個或者多個HTML表單,用來接收用戶輸入.可以輕鬆地將一個HTML表單從Serlet發送到瀏覽器.
HTML輸入域或文本域中的值將被當作字符串發送到服務器.空白的輸入域或文本域將發送一條空白的字符串,因此,帶有一個輸入域名稱的ServletRequest.getParameter將永遠不會返回null.

  • 接下來舉個栗子:FormServlet類示範了處理HTML表單的方法.它的doGet方法發送了一個給瀏覽器.它的doPost方法獲取輸入的值,並輸出它們.
package app01b;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "FormServlet", urlPatterns = { "/form" })
public class FormServlet extends HttpServlet {
    private static final long serialVersionUID = 54L;
    private static final String TITLE = "Order Form";

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<head>");
        writer.println("<title>" + TITLE + "</title></head>");
        writer.println("<body><h1>" + TITLE + "</h1>");
        writer.println("<form method='post'>");
        writer.println("<table>");
        writer.println("<tr>");
        writer.println("<td>Name:</td>");
        writer.println("<td><input name='name'/></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Address:</td>");
        writer.println("<td><textarea name='address' "
                + "cols='40' rows='5'></textarea></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Country:</td>");
        writer.println("<td><select name='country'>");
        writer.println("<option>United States</option>");
        writer.println("<option>Canada</option>");
        writer.println("</select></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Delivery Method:</td>");
        writer.println("<td><input type='radio' " +
                "name='deliveryMethod'"
                + " value='First Class'/>First Class");
        writer.println("<input type='radio' " +
                "name='deliveryMethod' "
                + "value='Second Class'/>Second Class</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Shipping Instructions:</td>");
        writer.println("<td><textarea name='instruction' "
                + "cols='40' rows='5'></textarea></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>&nbsp;</td>");
        writer.println("<td><textarea name='instruction' "
                + "cols='40' rows='5'></textarea></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Please send me the latest " +
                "product catalog:</td>");
        writer.println("<td><input type='checkbox' " +
                "name='catalogRequest'/></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>&nbsp;</td>");
        writer.println("<td><input type='reset'/>" +
                "<input type='submit'/></td>");
        writer.println("</tr>");
        writer.println("</table>");
        writer.println("</form>");
        writer.println("</body>");
        writer.println("</html>");
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<head>");
        writer.println("<title>" + TITLE + "</title></head>");
        writer.println("</head>");
        writer.println("<body><h1>" + TITLE + "</h1>");
        writer.println("<table>");
        writer.println("<tr>");
        writer.println("<td>Name:</td>");
        writer.println("<td>" + request.getParameter("name")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Address:</td>");
        writer.println("<td>" + request.getParameter("address")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Country:</td>");
        writer.println("<td>" + request.getParameter("country")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Shipping Instructions:</td>");
        writer.println("<td>");
        String[] instructions = request
                .getParameterValues("instruction");
        if (instructions != null) {
            for (String instruction : instructions) {
                writer.println(instruction + "<br/>");
            }
        }
        writer.println("</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Delivery Method:</td>");
        writer.println("<td>"
                + request.getParameter("deliveryMethod")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Catalog Request:</td>");
        writer.println("<td>");
        if (request.getParameter("catalogRequest") == null) {
            writer.println("No");
        } else {
            writer.println("Yes");
        }
        writer.println("</td>");
        writer.println("</tr>");
        writer.println("</table>");
        writer.println("<div style='border:1px solid #ddd;" +
                "margin-top:40px;font-size:90%'>");

        writer.println("Debug Info<br/>");
        Enumeration<String> parameterNames = request
                .getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String paramName = parameterNames.nextElement();
            writer.println(paramName + ": ");
            String[] paramValues = request
                    .getParameterValues(paramName);
            for (String paramValue : paramValues) {
                writer.println(paramValue + "<br/>");
            }
        }
        writer.println("</div>");
        writer.println("</body>");
        writer.println("</html>");
    }
}
  • 空白的訂單表單:
    這裏寫圖片描述
  • 填寫完表單,並單擊Submit(提交)按鈕.在表單中輸入的值將會通過HTTP POST方法發送到服務器,這樣就會調用Servlet的doPost方法.在訂單表單中輸入的值:
    這裏寫圖片描述

11.使用部署描述符

使用部署描述符是配置Servlet應用程序的另一種方法,部署描述符總是命名爲web.xml,並放在WEB-INF目錄下.

栗子:有兩個沒有@WebServlet進行標註的Servlet:分別爲:SimpleServlet和WelcomeServlet.以及部署描述符文件:web.xml:

  • 未標註的SimpleServlet類:
package app01c;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleServlet extends HttpServlet {
    private static final long serialVersionUID = 8946L;

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) 
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head>" +
                "<body>Simple Servlet</body></html");
    }
}
  • 未標註的WelcomeServlet類
package app01c;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WelcomeServlet extends HttpServlet {
    private static final long serialVersionUID = 27126L;

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) 
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<html><head></head>"
                + "<body>Welcome</body></html>");
    }
}
  • 部署描述符web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0"> 

    <servlet>
        <servlet-name>SimpleServlet</servlet-name>
        <servlet-class>app01c.SimpleServlet</servlet-class>
        <load-on-startup>10</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SimpleServlet</servlet-name>
        <url-pattern>/simple</url-pattern>
    </servlet-mapping>  


    <servlet>
        <servlet-name>WelcomeServlet</servlet-name>
        <servlet-class>app01c.WelcomeServlet</servlet-class>
        <load-on-startup>20</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>WelcomeServlet</servlet-name>
        <url-pattern>/welcome</url-pattern>
    </servlet-mapping>
</web-app>

部署描述符的好處:

  • 可以包含@WebServlet中沒有的元素,如load-on-startup表示在應用程序啓動時加載Servlet,而不是第一次調用Servlet時加載.
  • 如果需要修改配置值,如Servlet路徑,就不需要重新編譯Servlet類.
  • 可以將初始參數傳給一個Servlet,並且不需要重新編譯Servlet類就可以對它們進行編譯.
  • 部署描述符還允許覆蓋Servlet註解中指定的值.可以在標註完Servlet之後,又在同一個應用程序的部署描述符中聲明Servlet.

www.yaojiawei.cc

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