天天看點

JSP編譯成Servlet(三)JSP編譯後的Servlet

JSP編譯後的Servlet類會是怎樣的呢他們之間有着什麼樣的映射關系在探讨JSP與Servlet之間的關系時先看一個簡單的HelloWorld.jsp編譯成HelloWorld.java後會是什麼樣。

①HelloWorld.jsp

<%@ page contentType="text/html; charset=gb2312" language="java" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML>

    <HEAD>

    <TITLE>HelloWorld</TITLE>

    </HEAD>

<BODY>

<%

    out.println("HelloWorld");

%>

</BODY>

</HTML>

②HelloWorld_jsp.java

package org.apache.jsp;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.jsp.*;

public final class HelloWorld_jsp extends org.apache.jasper.runtime.HttpJspBase

    implements org.apache.jasper.runtime.JspSourceDependent {

  private static final javax.servlet.jsp.JspFactory _jspxFactory =

          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {

    return _jspx_dependants;

  }

  public void _jspInit() {

  public void _jspDestroy() {

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)

        throws java.io.IOException, javax.servlet.ServletException {

    final javax.servlet.jsp.PageContext pageContext;

    javax.servlet.http.HttpSession session = null;

    final javax.servlet.ServletContext application;

    final javax.servlet.ServletConfig config;

    javax.servlet.jsp.JspWriter out = null;

    final java.lang.Object page = this;

    javax.servlet.jsp.JspWriter _jspx_out = null;

    javax.servlet.jsp.PageContext _jspx_page_context = null;

    try {

      response.setContentType("text/html; charset=gb2312");

      pageContext = _jspxFactory.getPageContext(this, request, response,

       null, true, 8192, true);

      _jspx_page_context = pageContext;

      application = pageContext.getServletContext();

      config = pageContext.getServletConfig();

      session = pageContext.getSession();

      out = pageContext.getOut();

      _jspx_out = out;

      out.write("\r\n");

      out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n");

      out.write("<HTML>\r\n");

      out.write("    <HEAD>\r\n");

      out.write("    <TITLE>HelloWorld</TITLE>\r\n");

      out.write("    </HEAD>\r\n");

      out.write("<BODY>\r\n");

      out.println("HelloWorld");

      out.write("</BODY>\r\n");

      out.write("</HTML>\r\n");

    } catch (java.lang.Throwable t) {

      if (!(t instanceof javax.servlet.jsp.SkipPageException)){

        out = _jspx_out;

        if (out != null && out.getBufferSize() != 0)

          try { out.clearBuffer(); } catch (java.io.IOException e) {}

        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);

        else throw new ServletException(t);

      }

    } finally {

      _jspxFactory.releasePageContext(_jspx_page_context);

    }

}

經過前面介紹的文法解析及使用通路者模式對HelloWorld.jsp檔案編譯成相應的HelloWorld_jsp.java檔案可以看到Servlet類名是由jsp檔案名_jsp拼成。再往下看HelloWorld_jsp.java檔案的詳細内容類包名預設為org.apache.jsp預設有三個導入“import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;”。

接下去是真正的類主體jsp生成的java類都必須繼承org.apache.jasper.runtime.HttpJspBase這個類的結構圖如下繼承了HttpServlet是為了将HttpServlet的所有功能都繼承下來另外又實作HttpJspPage接口定義了一個JSP類的Servlet的核心處理方法_jspService除此之外還有_jspInit和_jspDestroy用于在jsp初始化和銷毀時執行這些方法其實都是由Servlet的service、init、destroy方法間接去調用是以jsp生成servlet主要就是實作這三個方法。

JSP編譯成Servlet(三)JSP編譯後的Servlet

除了繼承HttpJspBase外還需實作org.apache.jasper.runtime.JspSourceDependent接口這個接口隻有一個傳回Map<String,Long>類型的getDependants()方法Map的鍵值分别為資源名和最後修改時間這個實作主要是為了記錄某些依賴資源是否過時依賴資源可能是page指令導入的也可能是标簽檔案引用等。在生成servlet時如果jsp頁面做了上述依賴的話則會在servlet類中添加一個static塊static塊會将資源及最後修改時間添加到Map中。

在jsp類servlet處理過程中會依賴很多資源比如我要操作會話的話就需要此次通路的HttpSession對象比如我要操作Context容器級别的對象就要ServletContext對象再比如我要擷取servlet配置資訊就要ServletConfig對象最後還需要一個輸出對象用于在處理過程中将内容輸出。這些對象都在核心方法_jspService中使用作為servlet類要擷取這些對象其實非常簡單因為這些本身就屬于servlet屬性有相關方法直接擷取。但這裡因為JSP有自己的标準是以必須按照它的标準去實作。

具體的JSP标準是怎樣的首先為了友善JSP的實作提供一個統一的工廠類JspFactory用于擷取不同的資源其次由于按照标準規定不能直接使用servlet上下文是以需要定義一個PageContext類封裝servlet上下文最後同樣按照标準需要定義一個輸出類JspWriter封裝servlet的輸出。是以可以看到PageContext對象通過JspFactory擷取其他ServletContext對象、ServletConfig對象、HttpSession對象及JspWriter則通過PageContext對象擷取。通過這些對象再加上前面章節文法解析得到的文法樹對象再利用通路者模式對文法樹周遊就可以生成核心處理方法_jspService了。

上面隻是介紹了最簡單的一個jsp頁面轉變成servlet的過程旨在說明jsp到servlet轉化的原理實際上需要處理很多jsp指令标簽。

<a target="_blank" href="https://item.jd.com/12185360.html">點選訂購作者《Tomcat核心設計剖析》</a>