Tomcat服務器與Servlet

Tomcat服務器與Servlet

1. web概述

1.1 B/S與C/S

  • B/S:Browser/Server:瀏覽器-服務器架構。
  • C/S:Client/Server:客戶端-服務器架構。

優缺點比較:CS比BS更安全,BS比CS移植性更好

1.2 服務器

  • 軟件服務器:就是一個軟件。
  • 硬件服務器:安裝了軟件服務器的主機。

常見的JavaWeb服務器:WebLogic、Jboss、Tomcat、WebSphere

2. Tomcat服務器

2.1 Tomcat服務器目錄結構

|-bin  tom運行相關命令的目錄
|-conf  配置文件的目錄
|--server.xml  配置文件(主要用於配置端口、項目路徑)爲了能夠正常顯示中文,需要將server.xml文件的編碼方式改成utf-8
|-lib  tomcat依賴的jar包
|-logs  日誌文件夾
|-temp 
|-webapps  發佈的web項目
|-work  緩存的文件夾
  • 注意:將server.xml文件的編碼方式改成utf-8

2.2 Javaweb項目結構

|-webapp
|--靜態資源文件:html、css、js、圖片等
|--WEB-INF  WEB-INF文件夾下的資源不能直接訪問,只能通過代碼進行訪問
|---lib 存放依賴包的文件夾。
|---classes 編譯器中src文件夾現在源碼編譯後生成的class文件存放到這個目錄。(需要手動設置)
|---web.xml  web.xml文件的約束可以去tomcat中預置examples項目中去拷貝。

2.3 三種方式部署項目

2.3.1 拷貝到webapps

​ 拷貝項目的部署方式就是將整個項目目錄webapp目錄直接拷貝到tomcat服務器目錄下的webapps目錄下,然後通過http協議+ip地址+端口號+項目名的方式訪問即可。

2.3.2 新建work配置文件

​ 在tomcat服務器下的work/Catalina/localhost目錄中新建一個xml文件。啓動瀏覽器,通過http協議+ip地址+端口號+配置文件名的方式訪問即可。

<Context docBase="E:/web/webapp"/>

2.3.3 配置server.xml

<!--
	Context:代表上下文配置。
	docBase:代表項目的物理路徑。
	path:上下文路徑。
-->
<Context docBase="E:/web/webapp" path="/xx"/>
<!--  在瀏覽器通過localhost:8080/xx訪問項目   -->

3. Servlet

​ Servlet的規範是有javax包下的servlet接口制定的,它提供了幾個方法(這幾個方法代表了一個servlet的生命週期)。

package javax.servlet;
public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
    ServletConfig getServletConfig();
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    String getServletInfo();
    void destroy();
}

3.1 自定義Servlet

​ Java提供了一個javax.servlet接口,我們自定義的servlet類需要實現這個接口,並覆寫其中的方法。

public class HelloServlet implements Servlet {
    //構造方法,第一次訪問該servlet時調用的
    public HelloServlet(){
        System.out.println("constructor");
    }
	//初始化方法,在構造方法後調用
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init");
    }
	//獲得Servlet配置信息的方法
    @Override
    public ServletConfig getServletConfig() {
        System.out.println("getServletConfig");
        return null;
    }
	//servlet處理前臺請求的方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service");
    }
	////獲得Servlet的信息的方法
    @Override
    public String getServletInfo() {
        System.out.println("getServletInfo");
        return null;
    }
	//銷燬Servlet對象的方法,正常關閉服務器的時候執行
    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}
  • web.xml中配置自定義的servlet
<!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>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.yogie.lesson.HelloServlet</servlet-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

3.2 Servlet生命週期

①先執行構造方法 - 第一次訪問
②執行初始化方法(init) - 第一次訪問
③執行的服務方法(service)  - 第n次訪問
④對於tomcat來說,Servlet只有一個(它是單例的,使用的是緩存)
⑤當我們正常關閉tomcat的時候,執行destroy方法
⑥當瀏覽訪問的時候纔開始創建Servlet

3.3 ServletConfig接口

ServletConfig只封裝了4個方法:

String getInitParameter(String name) 根據web.xml中<init-param>標籤下的參數名稱獲取對應的值。
Enumeration getInitParameterNames() 獲取所有的參數名稱。
ServletContext getServletContext() 獲取servlet的上下文對象。
String getServletName() 獲取servlet的名稱(配置的servlet名稱,不是類名)
  • ServletContext接口:服務器啓動的時候,會爲託管的每一個web應用程序,創建一個ServletContext對象,從服務器移除託管,或者是關閉服務器。
Object getAttribute(String name) 
Object getAttribute(String name)
Enumeration getAttributeNames() 
ServletContext getContext(String uripath)
String getContextPath()
String getInitParameter(String name) 
Enumeration getInitParameterNames() 
RequestDispatcher getNamedDispatcher(String name) 
String getRealPath(String path) 
RequestDispatcher getRequestDispatcher(String path) 
URL getResource(String path) 
InputStream getResourceAsStream(String path) 
Set getResourcePaths(String path)  
String getServerInfo()
Servlet getServlet(String name) 
String getServletContextName()
Enumeration getServletNames() 
Enumeration getServlets() 
void removeAttribute(String name) 
void setAttribute(String name, Object object) 
  • Enumeration類:類似於迭代器,用於迭代獲取到的參數名稱。
boolean hasMoreElements() 測試此枚舉類是否還有下一個元素
E nextElement() 返回此枚舉的下一個元素。 

3.4 鉤子方法的設計

​ 鉤子方法的設計主要是模板方法設計模式的使用。模板方法設計模式:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。更進一步來說,模板方式模式就是通過不變的行爲搬移到超類,去除子類中的重複代碼來體現它的優勢。
​ 在應用方面上,當功能的內部一部分實現是確定的,但同時也有一些是不確定的,這時就可以將不確定的部分暴露出去,讓子類去實現。例如HttpServlet中service方法中確定的功能就是要將ServletRequest強制轉換成HttpServletRequest,而不確定的功能就是對請求的處理。

  • HttpServlet類的部分源碼:
/**
 * public abstract class GenericServlet implements Servlet, ServletConfig, Serializable
 * 抽象類GenericServlet實現了Servlet、ServletConfig、Serializable接口
 */
public abstract class HttpServlet extends GenericServlet {

    //對方法的參數進行強轉
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
            this.service(request, response);
        } else {
            throw new ServletException("non-HTTP request or response");
        }
    }

    //提供給子類覆寫的方法
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取前臺form表單的提交方式
        String method = req.getMethod();
        long lastModified;
        //如果前臺form表單以get方式提交,則調用doGet方法
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
            //以下同理
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }
    
    doGet()...doPost()...doDelete()...
    //doGet方法
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取http協議和版本,例如:HTTP/1.1
        String protocol = req.getProtocol();
        //定義異常信息
        String msg = lStrings.getString("http.method_get_not_supported");
        //如果是http1.1版本
        if (protocol.endsWith("1.1")) {
            //拋出405異常
            resp.sendError(405, msg);
        } else {
            //拋出400異常
            resp.sendError(400, msg);
        }
    }
}
  • 我們定義的子類僅僅需要覆寫前臺提交對應的方法即可。
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doDelete(req, resp);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
    }
}

代碼的執行流程分析:

​ 當瀏覽器訪問到當前的servlet類時,首先訪問的是類中帶ServletRequestServletResponse參數的service方法,但是此時子類沒有,就執行從父類(HttpServlet)繼承來的service方法,此時HttpServlet中的service方法將參數強轉爲HttpServletRequestHttpServletResponse,並且調用重載一個帶HttpServletRequestHttpServletResponse參數類型的service方法。在這個重載的方法內部會根據前臺的請求方法(get/post...)調用對應的(doGet/doPost...)方法。此時調用的方法即是子類覆寫父類的doXXX()方法,如果沒有覆寫對應的方法,那麼還是調用從父類繼承來的方法,由於父類的方法是直接返回的錯誤。

3. 處理請求

3.1 HttpServerRequest接口

​ 該接口繼承自ServletRequestServletRequest 對象提供包括瀏覽器傳遞的參數名稱、參數值、屬性和輸入流的數據。HttpServletRequestServletRequest類的基礎上提供了包含HTTP協議請求的相關信息。HttpServletRequest對象封裝了客戶端提交過來的一切數據。

String getParameter(String name) 根據請求參數名獲取前臺傳遞的參數值。如果參數可能擁有一個以上的值,則使用getParameterValues ()方法。
String[] getParameterValues(String name):根據參數名稱,獲取該參數的多個值。
Enumeration<String> getParameterNames():獲取所有請求參數的名字。
Map<String,String[]> getParameterMap():返回請求參數組成的Map集合。
String getContextPath() 返回上下文路徑(<Context path="上下文" ../>)。
String getCharacterEncoding() 返回此請求正文使用的字符編碼。
void setAttribute(String name, Object o) 返回指定名稱的屬性值。
void removeAttribute(String name) 從此請求中移除指定屬性。 
void setCharacterEncoding(String env) 重寫此請求正文中使用的字符編碼的名稱。必須在使用 getReader() 讀取請求參數或讀取輸入之前調用此方法。
Cookie[] getCookies() 返回包含客戶端隨此請求一起發送的所有 Cookie 對象的數組。如果沒有發送任何 cookie,則此方法返回 null。
String getProtocol()  獲取協議及版本號。
String getMethod() 獲取請求提交的方法。 
String getRequestURI() 返回當前請求的資源名稱,上下文路徑/資源名
StringBuffer getRequestURL() 返回瀏覽器地址欄的內容。
String getRemoteAddr():返回請求服務器的客戶端的IP
HttpSession getSession() 返回與此請求關聯的當前會話,如果該請求沒有會話,則創建一個會話。 
HttpSession getSession(boolean create) 返回與此請求關聯的當前 HttpSession,如果沒有當前會話並且 create 爲 true,則返回一個新會話。 如果 create 爲 false 並且該請求沒有有效的 HttpSession,則此方法返回 null。 
public RequestDispatcher getRequestDispatcher(String path)返回一個 RequestDispatcher 對象,它充當位於給定路徑上的資源的包裝器。可以使用 RequestDispatcher 對象將請求轉發給資源,或者在響應中包含資源。資源可以是動態的,也可以是靜態的。 

3.2 HttpServletResponse接口

​ 該接口繼承了ServletResponse接口,ServletResponse對象提供了將響應發送到瀏覽器的方法。

void setCharacterEncoding(String charset) 設置響應的編碼方式(寫在最前面)。
resp.setContentType("text/html;charset=UTF-8");  甚至的內容類型。
ServletOutputStream getOutputStream() 返回適用於在響應中編寫的二進制輸出流。
PrintWriter getWriter() 將字符文本返回給瀏覽器。
void addCookie(Cookie cookie) 將指定的cookie添加到響應。
void sendRedirect(String location) 使用指定重定向位置 URL 將臨時重定向響應發送到客戶端。此方法可以接受相對 URL;servlet 容器必須在將響應發送到客戶端之前將相對 URL 轉換爲絕對 URL。如果位置是相對的,沒有前導 '/',則容器將相對於當前請求 URI 對其進行解釋。如果位置是相對的,有一個前導 '/',則容器將相對於 servlet 容器根對其進行解釋。  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章