1. 程式人生 > >JSP的小心得

JSP的小心得

style 說明 ora cond this 轉變 void ner 成功

問題:Web容器(例如Tomcat)是怎麽來執行jsp文件的?

首先它會將放在webapps目錄下的jsp文件(這裏以hello.jsp為例)翻譯成hello_jsp.java文件並編譯為hello_jsp.class(註意:生成的文件名都是小寫字母),存放在Tomcat安裝目錄\work\Catalina\localhost\webDemo\org\apache\jsp路徑下,打開.java源文件,你會看到hello_jsp類繼承了httpJspBase類。而HttpJspBase類又是HttpServlet的子類,所以hello_jsp類也就間接實現了Servlet接口,也就是說Web容器把jsp文件轉換成了一個servlet類了


然後Web容器根據配置文件(web.xml)和客戶端(瀏覽器)的請求URL來確定使用哪一個servlet類.接下來調用這個servlet類的service()方法。在hello_jsp.java中看不到這個service()方法,並不代表它沒有這個方法,只代表它沒有重寫這個方法。(除了重寫之外,從父類繼承的方法在其子類中,都是隱藏的,所以你看不到。)那麽如果想看看在這個類中從其父類繼承得到的方法長什麽樣?很簡單!找到有這個方法的父類,查看方法的源碼即可。那麽如果需要調用這個類的service()方法怎麽辦?放心,No problem呀,它會去尋找所有父類裏有這個方法的父類,然後再執行這個父類的方法體(在這裏我們把這個機制或者行為笑稱為“兒子繼承的東西沒找到,咱不怕,去找老子

”),在這裏說明一下:在子類中調用繼承的方法,這個方法就是子類的方法,即SubClass.method(),只不過執行的方法體內容和其父類中的方法體內容一模一樣而已。那麽接著咱們就來說說這個父類HttpJspBase,在它的源碼中,你會看到兩個方法,第一個方法_japService(),被其子類hello_jsp類重寫了。另外一個方法,源碼如下

public void service(HttpServletRequest quest,HttpServletResponse response) throws ServletException,IOException{
  _jspService(request,response);
}

這個父類中的servce()方法,子類hello_jsp繼承得到的service方法也就是它。看到沒有,多麽簡單的一個方法呀!上面提過了Web容器在確定使用哪一個sevlet類後,接著要幹嘛?當然就是調用這個servlet的service()方法了,開始執行它的方法體,方法體中也沒啥復雜的事情,就是調用本類中的_japService()方法,也就是hello_jsp類中的重寫父類的_jspService()。

咱們接著看這個重寫的_jspService()的方法體,裏面有若幹個out.write();這個方法的實參不就是jsp文件的HTML內容嗎?哦,所以才有我們在瀏覽器上看到的輸出內容。

所以在父類裏定義的service()方法的作用我覺得很大:它把我們原始的繼承Serlet接口並重寫其service()方法的習慣轉變為了繼承HttpJspBase類並重寫其_jspService()方法。這是一種技術上的改進和分支吧,也就造就了JSP技術的發展吧。與之類似的是HttpServlet類,它也間接實現了Servlet接口,重寫了service()方法,特別是它繼承了元老級的GenericServlet(這個類可是直接實現Servlet接口的第一代類呀,可謂是親生的嫡長子呀!它提供了很多方法,可以說是一個重大改革,也為HttpServlet鋪好了路,關於它以後有空再講)。那麽我們來看看Httpservlet類實現的service()方法吧

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

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }

HttpServletRequest和HttpServletResponse分別是ServletRequest和ServletResponse的子類,這樣經過強轉型,再最後調用自己的service方法,成功完成了接口方法到基類方法的轉移了呀!還沒有完,接下來才是HttpServlet的高明之處:看看他自己定義的那個service()的源碼吧

    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;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // 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);
        }
    }

比較長,言而言之,就是這些個邏輯實現,將Web容器最先調用servlet類的service()方法轉移到了調用servlet類的doGet(),doPOST()等方法上,因為看不到這些底層的實現代碼,所以就讓我們產生了HttpServlet的入口是doGet()方法的假象。當然這也帶來了簡化流程的好處:繼承了HttpServlet類的子類只要重寫doGet (),doPOST()等方法即可。

總結:jsp實質上還是servlet,是一種加強版的servlet技術吧

JSP的小心得