Servlet 使用介紹(1)

說明

本篇介紹java web中比較重要的一個技術:servlet。servlet是一種對用戶請求動態響應的一個技術,是java web的核心一環。對於一般服務性質的純後臺服務應用而言,或許整個應用是由若干個servlet組成
,而其他方面表現較弱。

servlet

一般的我們的應用都會發布到一個服務應用中,即一個servlet容器中,常見的servlet容器有:Tomcat,JBoss,WebLogic等。我們通過servlet容器處理用戶請求,當用戶向我們的某個servlet發起請求時,servlet容器會調用並轉發請求結果到該servlet,同時會將servlet返回結果回饋給請求方(用戶)。其實,servlet本質就是一個接口,我們可以通過註冊servlet服務,實現servlet接口,用於監聽各種狀態先的信息交互。如下,僅僅介紹tomcat中的servlet的相關使用(主要是其他也沒用過)。

Tomcat中爲servlet的使用提供了一個調用的jar包:sevlet-api.jar ,該jar包位於Tomcat根目錄的lib目錄下(所有和Tomcat相關的Jar包均放在這裏),如下爲servlet-api.jar中的包信息:

servlet-api.jar
--javax.servlet           //核心
--javax.servlet.annotation
--javax.servlet.descriptor
--javax.servlet.http     //基於http的實現

可以看到,這裏主要石油javax.servlet和javax.servlet.http兩個包組成實現。其中javax.servlet.Servlet接口爲api的核心,servlet容器通過調用其定義方法,實現網絡中客戶端和服務端之前通信的中轉傳遞,Servlet提供了五個方法,其中三個方法是由servlet容器調用。如下,servlet的源碼:



package javax.servlet;

import java.io.IOException;

public interface Servlet {
    /**
     *由sevler容器調用,servlet對象初始化後,servlet容器調用此方法
     *
     */
    public void init(ServletConfig config) throws ServletException;

    /**
     *提供的一個獲取ServletConfig對象的方法,該方法由我們自己實現,返回的對象即是init
     *的方法傳入的參數。
     *
     */
    public ServletConfig getServletConfig();

    /**
     *該方法需要在init方法成功調用後纔可正常調用(即客戶端請求的時候必須姚該servlet以及
     *初始化成功)
     *
     */
    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;



    /**
     *返回當前servlet的創建信息包括創建者等(爲一個String字串)
     *
     */
    public String getServletInfo();

    /**
     *由servlet容器調用,當一個servlet容器被銷燬時調用,可以用於清理銷燬servlet中的佔
     *用的一些資源。
     *
     */
    public void destroy();
}

Servlet接口爲最基礎的Servlet組件,可供我們最大限度的對servlet進行定製.servlet-api.jar中也爲我們提供了它的兩個替代實現:一個是javax.servlet.GenericServlet是其實現類,完成它的最基礎的一些邏輯實現,但保留其對於網絡通信的處理,交由我們自行處理;另一個是javax.servlet.http.HttpServlet,該類是GenericServlet的子類,是對GenericServlet的一種擴展,其對於Servlet的servive方法進行了處理,主要用於Http請求。如下,給出這兩個類的實現源碼:

  1. GenericServlet

該抽象類並未對servive方法作處理。

public abstract class GenericServlet implements Servlet, ServletConfig,  
                      java.io.Serializable{

    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

    private transient ServletConfig config;



    public GenericServlet() { }


    /**
     *銷燬方法
     */
    public void destroy() {
    }


    /**
     *獲取servlet初始化傳入參數
     *
     */
    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);
    }


   /**
    *servlet參數參數名獲取
    */
    public Enumeration<String> getInitParameterNames() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getInitParameterNames();
    }   


    /**
     *實現Servlet方法返回初始化獲取的ServletConfig對象
     */    
    public ServletConfig getServletConfig() {
               return config;
    }


    /**
     *獲取ServletContext對象(Servlet上下文),是在ServletConfig中獲取
     */
    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 "";
    }


    /**
     *實現Servlet初始化方法,並回調init()無參方法,可供實現者監聽狀態.
     *
     *
     */
    public void init(ServletConfig config) throws ServletException {
          this.config = config;
           this.init();
    }


    /**
     * 子類可通過重寫監聽Servlet對象初始化狀態.
     */
    public void init() throws ServletException {

    }


    public void log(String msg) {
           getServletContext().log(getServletName() + ": "+ msg);
    }



    public void log(String message, Throwable t) {
         getServletContext().log(getServletName() + ": " + message, t);
    }


    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();
    }
}
  1. HttpServlet

是GenericServlet的子類,對service添加了Http的處理,一般適用於Http通信的Servlet,是較爲常用的一種Servlet.

package javax.servlet.http;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public abstract class HttpServlet extends GenericServlet{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";

    private static final String LSTRING_FILE =
        "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);



    public HttpServlet() { }


    /**
     *Http協議Get方式請求回調
     *
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    protected long getLastModified(HttpServletRequest req) {
        return -1;
    }


    /**
     *Http協議HHEAD(只請求頁面的頭部)方式請求回調
     */
    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        NoBodyResponse response = new NoBodyResponse(resp);

        doGet(req, response);
        response.setContentLength();
    }


    /**
     *Http協議POST方式請求回調
     */
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    /**
     *Http協議Put方式請求回調
     */
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_put_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    /**
     *Http協議Delete方式請求回調
     */
    protected void doDelete(HttpServletRequest req,
                            HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_delete_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }


    private Method[] getAllDeclaredMethods(Class<?> c) {

        if (c.equals(javax.servlet.http.HttpServlet.class)) {
            return null;
        }

        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
        Method[] thisMethods = c.getDeclaredMethods();

        if ((parentMethods != null) && (parentMethods.length > 0)) {
            Method[] allMethods =
                new Method[parentMethods.length + thisMethods.length];
            System.arraycopy(parentMethods, 0, allMethods, 0,
                             parentMethods.length);
            System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
                             thisMethods.length);

            thisMethods = allMethods;
        }

        return thisMethods;
    }



    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        Method[] methods = getAllDeclaredMethods(this.getClass());

        boolean ALLOW_GET = false;
        boolean ALLOW_HEAD = false;
        boolean ALLOW_POST = false;
        boolean ALLOW_PUT = false;
        boolean ALLOW_DELETE = false;
        boolean ALLOW_TRACE = true;
        boolean ALLOW_OPTIONS = true;

        for (int i=0; i<methods.length; i++) {
            Method m = methods[i];

            if (m.getName().equals("doGet")) {
                ALLOW_GET = true;
                ALLOW_HEAD = true;
            }
            if (m.getName().equals("doPost"))
                ALLOW_POST = true;
            if (m.getName().equals("doPut"))
                ALLOW_PUT = true;
            if (m.getName().equals("doDelete"))
                ALLOW_DELETE = true;

        }

        String allow = null;
        if (ALLOW_GET)
            allow=METHOD_GET;
        if (ALLOW_HEAD)
            if (allow==null) allow=METHOD_HEAD;
            else allow += ", " + METHOD_HEAD;
        if (ALLOW_POST)
            if (allow==null) allow=METHOD_POST;
            else allow += ", " + METHOD_POST;
        if (ALLOW_PUT)
            if (allow==null) allow=METHOD_PUT;
            else allow += ", " + METHOD_PUT;
        if (ALLOW_DELETE)
            if (allow==null) allow=METHOD_DELETE;
            else allow += ", " + METHOD_DELETE;
        if (ALLOW_TRACE)
            if (allow==null) allow=METHOD_TRACE;
            else allow += ", " + METHOD_TRACE;
        if (ALLOW_OPTIONS)
            if (allow==null) allow=METHOD_OPTIONS;
            else allow += ", " + METHOD_OPTIONS;

        resp.setHeader("Allow", allow);
    }


    /**
     *處理TRACE請求
     */
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {

        int responseLength;

        String CRLF = "\r\n";
        StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI())
            .append(" ").append(req.getProtocol());

        Enumeration<String> reqHeaderEnum = req.getHeaderNames();

        while( reqHeaderEnum.hasMoreElements() ) {
            String headerName = reqHeaderEnum.nextElement();
            buffer.append(CRLF).append(headerName).append(": ")
                .append(req.getHeader(headerName));
        }

        buffer.append(CRLF);

        responseLength = buffer.length();

        resp.setContentType("message/http");
        resp.setContentLength(responseLength);
        ServletOutputStream out = resp.getOutputStream();
        out.print(buffer.toString());
    }


    /**
     *http協議處理
     */
    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);
        }
    }



    private void maybeSetLastModified(HttpServletResponse resp,
                                      long lastModified) {
        if (resp.containsHeader(HEADER_LASTMOD))
            return;
        if (lastModified >= 0)
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }


    /**
     *重寫父類方法,轉化爲http協議處理,調用自有http處理
     */
    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);
    }
}


// file private
class NoBodyResponse extends HttpServletResponseWrapper {

    private static final ResourceBundle lStrings
        = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");

    private NoBodyOutputStream noBody;
    private PrintWriter writer;
    private boolean didSetContentLength;
    private boolean usingOutputStream;

    // file private
    NoBodyResponse(HttpServletResponse r) {
        super(r);
        noBody = new NoBodyOutputStream();
    }

    // file private
    void setContentLength() {
        if (!didSetContentLength) {
            if (writer != null) {
                writer.flush();
            }
            setContentLength(noBody.getContentLength());
        }
    }

    public void setContentLength(int len) {
        super.setContentLength(len);
        didSetContentLength = true;
    }

    public ServletOutputStream getOutputStream() throws IOException {

        if (writer != null) {
            throw new IllegalStateException(
                lStrings.getString("err.ise.getOutputStream"));
        }
        usingOutputStream = true;

        return noBody;
    }

    public PrintWriter getWriter() throws UnsupportedEncodingException {

        if (usingOutputStream) {
            throw new IllegalStateException(
                lStrings.getString("err.ise.getWriter"));
        }

        if (writer == null) {
            OutputStreamWriter w = new OutputStreamWriter(
                noBody, getCharacterEncoding());
            writer = new PrintWriter(w);
        }

        return writer;
    }
}


/*
 * Servlet output stream that gobbles up all its data.
 */
// file private
class NoBodyOutputStream extends ServletOutputStream {

    private static final String LSTRING_FILE =
        "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

    private int contentLength = 0;

    // file private
    NoBodyOutputStream() {}

    // file private
    int getContentLength() {
        return contentLength;
    }

    public void write(int b) {
        contentLength++;
    }

    public void write(byte buf[], int offset, int len)
        throws IOException
    {
        if (len >= 0) {
            contentLength += len;
        } else {
            // This should have thrown an IllegalArgumentException, but
            // changing this would break backwards compatibility
            throw new IOException(lStrings.getString("err.io.negativelength"));
        }
    }
}

如上,Servlet對於用戶的請求獲取和返回主要是通過方法:

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

實現的。這裏,servlet容器在調用時傳入兩個參數:一個ServletRequest對象,用於接收用戶請求信息;一個ServletResponse對象,用於返回請求用戶信息。而HttpServlet對於對象則更改爲HttpServletRequest和HttpServletResponse對象。其實這兩個接口是同樣在對應的ServletRequest和ServletResponse接口的基礎上擴展的。

Enjoytoday,EnjoyCoding

發佈了52 篇原創文章 · 獲贊 43 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章