Servlet - 基礎

Servlet

標籤 : Java與Web


HTTP協議

HTTP(hypertext transport protocol),即超文本傳輸協議.這個協議詳細規定了瀏覽器(Browser)和萬維網服務器(WebServer)之間互相通信的規則.其主要特點可簡單概括如下:
1) 簡單快速: 客戶端向服務器請求服務時,只需傳送請求方法和路徑, 因此使得HTTP服務器的程序規模小,通信速度快;
2) 靈活: HTTP允許傳輸任意類型的數據對象(傳輸類型由Content-Type控制);
3) 無連接: 無連接的含義是限制每次連接只處理一個請求;
4) 無狀態: 無狀態是指協議對於事務處理沒有記憶能力(如果後續處理需要前面的信息,則必須重傳.這樣可能導致每次連接傳送的數據量增大.但如果在服務器不需要先前信息時它的應答就會非常快快).


HTTP請求

一個HTTP請求通常包含三部分(中間已空行隔開):

請求行:        (方法 /統一資源標識符URI/協議/版本)
請求頭:        (Accept/Accept-Language等)
空行:     (CRLF)
請求體:        (攜帶的數據信息, GET請求沒有)

HTTP請求可以使用HTTP標準中定義的所有請求類型, HTTP1.1支持7種請求類型, 但在互聯網應用中最爲常用的只有GETPOST.


HTTP-GET

GET /WeChat/cc3200/get_status.do HTTP/1.1
Host: aliyun
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
  • 請求頭解析
請求頭 描述
User-Agent 瀏覽器與操作系統信息
Accept 當前瀏覽器可以接收的文檔類型
Accept-Language 當前瀏覽器支持的語言
Accept-Encoding 當前瀏覽器支持的壓縮格式:服務器會把數據壓縮後再發送到網絡中傳輸
Accept-Charset 當前瀏覽器支持的編碼
Connection 當前瀏覽器支持的連接方式(keep-alive即保持一段時間的連接,默認爲3000ms)
Cookie 如果不是第一次訪問該網址,可能會在請求中把上次服務器響應的Cookie數據一併發送過去

HTTP-POST

POST /WeChat/cc3200/get_status.do HTTP/1.1
Content-Length: 36
Cache-Control: max-age=0
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:8080/test/
...

user_name=feiqing&user_password=pass
  • 請求頭解析
請求頭 描述
Referer 表明請求來自哪個頁面
Content-Type application/x-www-form-urlencoded:表單數據類型,說明會使用URL編碼來格式化數據
Content-Length 請求體長度
user_name=feiqing&user_password=pass 請求體: 請求攜帶的數據

HTTP響應

一個HTTP響應通常也包含三部分(中間已空行隔開):

響應行:        (協議/狀態碼/描述)
響應頭:        (Server/Content-Length/Set-Cookie等)
空行:     (CRLF)
響應體:        (攜帶的數據)

HTTP響應是由服務器發送給瀏覽器的數據,瀏覽器會根據HTTP響應來解析並顯示內容:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 8
Date: Sun, 17 Apr 2016 12:39:11 GMT

<html>
    ...
</html>
  • 響應頭解析
響應頭 描述
Server 服務器信息
Content-Length 響應實體長度
Set-Cookie 響應給客戶端的Cookie
Expires: -1; / Cache-Control: no-cache; / Pragma: no-cache; 設置瀏覽器不要緩存數據
Refresh 自動刷新頁面

在HTML文件中可用<meta/>標籤來設置響應頭信息:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

響應狀態碼

狀態碼說明了響應的真正含義:

狀態 描述
200 請求成功
404 請求資源沒找到
500 服務器內部錯誤
302 重定向: 表示服務器要求瀏覽器重新再發一個請求到服務器指定的一個Location
304 緩存未過期(服務器資源未曾修改), 詳細可參考理解HTTP/304響應

Tomcat

Tomcat是一個免費開源的Serlvet容器,它是Apache基金會的Jakarta項目中的一個核心項目,由Apache,Sun和其它一些公司及個人共同開發而成. 由於有了Sun的參與和支持, 因此最新的Servlet和Jsp規範總能在Tomcat中得到體現.主頁:http://tomcat.apache.org/.


Tomcat目錄結構

  • bin: 存放可執行腳本文件(如startup.bat/startup.sh等)
  • conf: 存放Tomcat相關配置文件:
    • server.xml: 整個Tomcat運行環境配置(如端口號/虛擬主機等)
    • web.xml: 部署描述符文件(定義了默認JSP/Servlet處理規則,是所有web項目中WEB-INF/web.xml的父文件)
    • context.xml: 對所有應用的統一配置.
  • lib:Tomcat類庫, 該目錄中的jar包所有項目共享.
  • logs : Tomcat日誌目錄.
  • webapps:存放WEB應用,其每個子目錄都是一個項目;
  • work:運行時生成的文件.

server.xml

<Server port="8005" shutdown="SHUTDOWN">
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>
  • 元素解析
元素 描述
<Server/> 根元素,整個Tomcat的配置信息
<Service/> 服務(在<Server/>中只能有一個<Service/>)
<Connector/> 連接
<Engine/> 引擎,是<Service/>組件核心
<Host/> 每個<Host/>元素表示一臺虛擬主機.每臺虛擬主機都有自己的主機名和項目目錄
<Context/> 每個<Context/>元素表示一個應用.如果應用在<Host/>的appBase指定的目錄下,那麼可以不配置<Context/>元素,如果是外部應用,那麼就必須配置<Context/>

Tomcat配置

1. 配置端口號

編輯%CATALANA_HOME%\conf\server.xml文件中的<Connector/>元素

<!-- A "Connector" represents an endpoint by which requests are received
     and responses are returned. Documentation at :
     Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
     Java AJP  Connector: /docs/config/ajp.html
     APR (HTTP/AJP) Connector: /docs/apr.html
     Define a non-SSL HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

2. 配置外部應用

配置外部應用之後, 項目就可以不用拷貝到webapps目錄下,自定義項目存放位置,其配置方式有兩種:

  • 1: 修改server.xml
    <Host/>元素中添加<Context/>元素
  <Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">
    <Context path="/test/" docBase="/home/www/test"/>

    <!-- Access log processes all example.
         Documentation at: /docs/config/valve.html
         Note: The pattern used is equivalent to using pattern="common" -->
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="localhost_access_log." suffix=".txt"
           pattern="%h %l %u %t &quot;%r&quot; %s %b" />

  </Host>

如果指定path爲空(path=”“), 則默認訪問的項目就是/home/www/test, 而不再是webapps下的ROOT.

  • 2: 編輯conf/catalana/localhost目錄:
    新增test.xml文件
<Context docBase="/home/www/test"/>

存放到%CATALANA_HOME%/conf/catalana/localhost目錄下, 文件名即爲應用名.


Servlet

Servlet技術核心就是Servlet接口,所有Servlet實現類都必須實現Servlet接口,Servlet容器(如Tomcat)會把Servlet類加載到內存並生成唯一實例,當有請求到來時調用其方法.

  • 實現Servlet方式有三種:
    1. 實現javax.servlet.Servlet接口
    2. 繼承javax.servlet.GenericServlet
    3. 繼承javax.servlet.http.HttpServlet

Servlet

Servlet接口定義如下

public interface Servlet {

    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();

    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;

    public String getServletInfo();

    public void destroy();
}
方法 描述
init 在第一次請求該Servlet(默認)或容器啓動時, Servlet容器就會調用init(), 且只調用一次
service 每次請求Servlet都會調用該方法
destroy 銷燬Servlet時(卸載應用/關閉容器時), 調用該方法
  • HelloServlet
/**
 * @author jifang.
 * @since 2016/4/17 8:32.
 */
public class HelloServlet implements Servlet {

    private ServletConfig config;

    public void init(ServletConfig config) throws ServletException {
        System.out.println("init()...");
        this.config = config;
        System.out.println("config: <" + config + ">");
    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("service()...");
        System.out.println("req: <" + req + ">, res: <" + res + ">");
        res.getWriter().print("<h1>HelloServlet</h1>");
    }

    public void destroy() {
        System.out.println("destroy()...");
    }


    public ServletConfig getServletConfig() {
        return this.config;
    }

    public String getServletInfo() {
        return null;
    }
}
  • web.xml
<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"
         metadata-complete="true">
    <display-name>JavaWeb</display-name>
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello_servlet.do</url-pattern>
    </servlet-mapping>
</web-app>
  1. url-pattern
    <url-pattern/>用來指定Servlet的訪問路徑,必須以/開頭.

    • 可以在<servlet-mapping/>配置多個<url-pattern/>, 此時一個Servlet實例就綁定多個URL.
    • 可以在<url-pattern/>中使用通配符*,可以使一個Servlet綁定一組URL, 但*不能出現在中間位置,也不能只有*通配符, 另外, 通配符只是一種模糊匹配URL的方式,如果存在更具體的<url-pattern/>,那麼會優先選擇精確匹配.
  2. 配置在容器啓動時創建Servlet實例

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<load-on-startup/>元素可以讓容器在啓動時就創建該Servlet實例(調用init()方法),注意<load-on-startup/>元素的值必須是>=0的整數,它代表容器啓動時創建Servlet實例的順序.


GenericService

GenericService抽象類實現了Servlet接口並完成以下工作:
1. 將init()方法中的ServletConfig賦給一個實例變量, 使他可以通過getServletConfig()來獲取.
2. 爲Servlet接口的所有方法提供默認實現.
3. 提供方法來包裝ServletConfig.

  • Generic部分代碼
public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable
{
    private transient ServletConfig config;

    public GenericServlet() { }

    public void destroy() {
    }

    public String getInitParameter(String name) {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getInitParameter(name);
    }

    public Enumeration<String> getInitParameterNames() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getInitParameterNames();
    }   


    public ServletConfig getServletConfig() {
    return config;
    }

    public ServletContext getServletContext() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getServletContext();
    }

    public String getServletInfo() {
    return "";
    }

    public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
    }

    public void init() throws ServletException {

    }

    public abstract void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;

    public String getServletName() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getServletName();
    }
}

HttpServlet

HttpServletGenericServlet的子類,它提供了對HTTP協議的支持.覆蓋了GenericServletservice()方法,並新增了接受HttpServletRequest/HttpServletResponse參數的service()方法:

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;

    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;

    service(request, response);
}


protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}
  • 原始的service()將請求/響應向下轉型爲HttpServletRequest/HttpServletResponse, 並調用新的service(). 由於HttpServlet在新的service()方法中已經做了很多工作, 因此在繼承HttpServlet實現自動以Servlet時, 則只需覆蓋doGet()/doPost()等即可, 而沒有必要覆蓋service()(極少數情況需要覆蓋doHead()等)

注意: Request/Response向下轉型總會成功:因爲在調用service()方法時,Servlet容器總會預計使用HTTP,從而直接創建並傳遞HttpServletRequest/HttpServletResponse實例.

  • HelloHttpServlet
/**
 * @author jifang.
 * @since 2016/4/20 19:48.
 */
@WebServlet(name = "HelloHttpServlet", urlPatterns = "/hello_http_servlet.do")
public class HelloHttpServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doPost() ...");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doGet() ...");
    }
}

HttpServletRequest

對於每一個HTTP請求, Servlet容器會在調用service()方法時創建Request實例並傳遞給service形參, HttpServletRequest是Request在HTTP環境下的實例,其封裝了有關請求的信息:

  • 封裝請求頭信息;
  • 封裝請求正文數據(GET沒有正文);
  • 提供請求轉發/包含功能;
  • 作爲域對象, 可以傳遞數據.

獲取請求頭

方法 描述
String getHeader(String name) Returns the value of the specified request header as a String.
Enumeration<String> getHeaderNames() Returns an enumeration of all the header names this request contains.
long getDateHeader(String name) Returns the value of the specified request header as a long value that represents a Date object.
Enumeration<String> getHeaders(String name) Returns all the values of the specified request header as an Enumeration of String objects.
int getIntHeader(String name) Returns the value of the specified request header as an int.
String getRemoteAddr() Returns the Internet Protocol (IP) address of the client or last proxy that sent the request.
String getMethod() Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT.
String getContextPath() Returns the portion of the request URI that indicates the context of the request.
  • 獲取請求來源
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String referer = request.getHeader("Referer");
    String userAgent = request.getHeader("User-Agent");
    composeResponse(referer, userAgent, response);
}

private void composeResponse(String referer, String userAgent, HttpServletResponse response) throws IOException {
    response.setHeader("Content-Type", "text/html;charset=utf-8");
    PrintWriter writer = response.getWriter();
    if (!Strings.isNullOrEmpty(referer)) {
        writer.print("<h1>來源地址: " + referer + "</h1>");
    } else {
        writer.print("<h1>來自瀏覽器地址欄</h1>");
    }

    writer.print("<hr>");
    writer.print("<h1>來源信息: " + userAgent + "</h1>");
}

獲取請求參數

方法 描述
String getParameter(String name) Returns the value of a request parameter as a String, or null if the parameter does not exist.
Map<String,String[]> getParameterMap() Returns a java.util.Map of the parameters of this request.
Enumeration<String> getParameterNames() Returns an Enumeration of String objects containing the names of the parameters contained in this request.
String[] getParameterValues(String name) Returns an array of String objects containing all of the values the given request parameter has, or null if the parameter does not exist.
  • 獲取微信請求消息
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    Element root;
    String xml = request.getParameter("xml");
    try {
        if (!Strings.isNullOrEmpty(xml)) {
            root = new SAXReader().read(new StringReader(xml)).getRootElement();
        } else {
            String data = CharStreams.toString(new InputStreamReader(request.getInputStream()));
            root = new SAXReader().read(new StringReader(data)).getRootElement();
        }
    } catch (DocumentException | IOException e) {
        LOGGER.error("parse wx xml error", e);
        throw new RuntimeException();
    }

    // ...
}

請求轉發/包含

Request提供了getRequestDispatcher()來獲取一個RequestDispatcher, 並由其提供請求轉發/請求包含功能.

Request方法 描述
RequestDispatcher getRequestDispatcher(String path) Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path.

請求轉發/請求包含都是由多個Servlet協作完成一個請求, 因此需要從一個Servlet中跳到另一個Servlet中:

RequestDispatcher方法 描述
void include(ServletRequest request, ServletResponse response) Includes the content of a resource (servlet, JSP page, HTML file) in the response.
void forward(ServletRequest request, ServletResponse response) Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server.
  • 請求轉發: 原Servlet只會保留設置的響應頭信息.
  • 請求包含: 原Servlet既會保留響應頭, 還會保留響應體內容.

注意: 請求轉發時, 可能會因爲原Servlet設置了過多的響應體內容導致拋出異常java.lang.IllegalStateException: Cannot forward after response has been committed


域對象傳遞數據

由於請求轉發/請求包含都只是一次請求, 因此在多個Servlet之間都是共用一個Reqeust, 因此可以利用Request的在多個Servlet之間共享數據:

方法 描述
Object getAttribute(String name) Returns the value of the named attribute as an Object, or null if no attribute of the given name exists.
Enumeration<String> getAttributeNames() Returns an Enumeration containing the names of the attributes available to this request.
void setAttribute(String name, Object o) Stores an attribute in this request.
void removeAttribute(String name) Removes an attribute from this request.

HttpServletResponse

同Request, Servlet容器會在每次調用service()方法時創建Response實例並傳遞給service()形參, HttpServletResponse是Response綁定在HTTP環境下的實例, 其隱藏了將響應發送給瀏覽器的複雜性:

  • 設置響應狀態碼;
  • 設置響應頭信息;
  • 設置響應正文;

設置響應狀態碼

方法 描述
void setStatus(int sc) Sets the status code for this response.
void sendError(int sc) Sends an error response to the client using the specified status code and clears the buffer.
void sendError(int sc, String msg) Sends an error response to the client using the specified status and clears the buffer.

關於狀態碼的描述, 詳見HTTP協議部分介紹, 在此就不再贅述.

  • 響應404
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // response.sendError(404, "nothing!!");
    response.setStatus(404);
}

設置響應頭信息

方法 描述
void setHeader(String name, String value) Sets a response header with the given name and value.
void addHeader(String name, String value) Adds a response header with the given name and value.
void setIntHeader(String name, int value) Sets a response header with the given name and integer value.
void addIntHeader(String name, int value) Adds a response header with the given name and integer value.
void addDateHeader(String name, long date) Adds a response header with the given name and date-value.
void setDateHeader(String name, long date) Sets a response header with the given name and date-value.
void sendRedirect(String location) Sends a temporary redirect response to the client using the specified redirect location URL and clears the buffer.

關於HTTP響應頭的描述, 詳見HTTP協議部分介紹, 在此就不再贅述.

  • 設置禁用瀏覽器緩存(Cache-Control, pragma, expires)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setHeader("Cache-Control", "no-cache");
    response.setHeader("pragma", "no-cache");
    response.setDateHeader("expires", -1);
}
  • 設置重定向(302, Location)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
    response.setHeader("Location", "http://www.baidu.com");
}

HttpServletResponse還提供了另外一種重定向的方式, 直接使用sendRedirect()方法, 避免了以上的步驟:

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.sendRedirect("http://www.baidu.com");
}

設置響應正文

Response提供瞭如下兩個方法來獲取輸出流對象以響應HTTP正文內容

方法 描述
ServletOutputStream getOutputStream() Returns a ServletOutputStream suitable for writing binary data in the response.
PrintWriter getWriter() Returns a PrintWriter object that can send character text to the client.

OutputStream傳輸二進制數據流(字節數據), 常用作文件下載; Writer傳輸字符數據, 常用作響應HTTP正文內容(如HTML/XML等).

注意: 在一個請求中,不能同時使用這兩個流, 否則會拋出IllegalStateException.

字符響應流
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    PrintWriter writer = response.getWriter();
    writer.print("<html>");
    writer.print("<h1>content</h1>");
    writer.print("</html>");
}
  • 緩衝區
    PrintWriter的默認緩衝區大小爲8K, 因此當響應數據大小<8K時, 數據存放在緩衝區, 而不會立刻發送到瀏覽器, 直到Servlet執行結束,因此如果希望馬上發送給瀏覽器, 需要調用Response的flushBuffer()方法手動刷新緩衝區.

ServletConfig

在容器初始化Servlet時, 會將一個ServletConfig實例傳給init()方法,其封裝了@WebServlet/部署描述符傳遞給Servlet的配置信息:

方法 描述
String getInitParameter(String name) Gets the value of the initialization parameter with the given name.
Enumeration<String> getInitParameterNames() Returns the names of the servlet’s initialization parameters as an Enumeration of String objects.
ServletContext getServletContext() Returns a reference to the ServletContext in which the caller is executing.
  • java
public void init(ServletConfig config) throws ServletException {
    this.config = config;

    Enumeration<String> names = config.getInitParameterNames();
    while (names.hasMoreElements()) {
        String name = names.nextElement();
        String value = config.getInitParameter(name);

        System.out.println(name + " -> " + value);
    }
}
  • web.xml
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.fq.web.servlet.HelloServlet</servlet-class>
        <init-param>
            <param-name>admin</param-name>
            <param-value>com.fq</param-value>
        </init-param>
        <init-param>
            <param-name>e-mail</param-name>
            <param-value>[email protected]</param-value>
        </init-param>
    </servlet>

ServletContext

ServletConfig中提供了獲取ServletContext的方法getServletContext(), ServletContext代表Servlet應用程序,且每個應用程序僅有一個ServletContext實例,其在容器啓動時創建, 在容器關閉時銷燬, 因此可以利用其在多個Servlet中傳遞數據.
所有域對象都有存取數據的功能,因爲域對象內部有一個Map,用來存儲數據,下面是ServletContext對象用來操作數據的方法:

方法 描述
void setAttribute(String name, Object object) Binds an object to a given attribute name in this ServletContext.
Object getAttribute(String name) Returns the servlet container attribute with the given name, or null if there is no attribute by that name.
Enumeration<String> getAttributeNames() Returns an Enumeration containing the attribute names available within this ServletContext.
void removeAttribute(String name) Removes the attribute with the given name from this ServletContext.

應用初始化參數

前面看到ServletConfig可以獲取針對本Servlet的初始化參數,而利用ServletContext可以獲取針對本應用程序的公共初始化參數:

方法 描述
String getInitParameter(String name) Returns a String containing the value of the named context-wide initialization parameter, or null if the parameter does not exist.
Enumeration<String> getInitParameterNames() Returns the names of the context’s initialization parameters as an Enumeration of String objects, or an empty Enumeration if the context has no initialization parameters.
  • web.xml
<context-param>
    <param-name>admin</param-name>
    <param-value>feiqing</param-value>
</context-param>
<context-param>
    <param-name>e-mail</param-name>
    <param-value>zhujifang666@163.com</param-value>
</context-param>
  • java
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletContext context = getServletContext();
    String admin = context.getInitParameter("admin");
    String email = context.getInitParameter("e-mail");
    System.out.printf("admin: %s%n", admin);
    System.out.printf("e-mail: %s%n", email);
}

獲取資源

可以使用ServletContext來獲取Web應用下的資源路徑/資源流等內容:

方法 描述
String getRealPath(String path) Gets the real path corresponding to the given virtual path.
URL getResource(String path) Returns a URL to the resource that is mapped to the given path.
InputStream getResourceAsStream(String path)` Returns the resource located at the named path as an InputStream object.
Set<String> getResourcePaths(String path) Returns a directory-like listing of all the paths to resources within the web application whose longest sub-path matches the supplied path argument.

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