JSP概述與運行原理

這篇文章猶豫了很久,還是覺得提起筆來,好好地來講解下JSP。

相信大家也都有所聽聞,不管是視頻上、博客中、公司裏,都有着這麼一個聲音:這都21世紀二十年代,JSP這種技術還有什麼學習的價值。誠然,這句話是有一定的道理的,隨着技術的不斷創新和軟件設計思想的變革,首先是Velocity、Freemarker、Thymeleaf等模板引擎的出現來擠壓JSP的空間,而前後端分離的架構徹底的讓JSP失去了生存的土壤。於是一代傳奇,早期Web開發中的旗手,慢慢的淡出了開發人員的視野。

但是,回過頭來,我們再來看JSP是否還需要學這個問題。首先一點就是,當你點開這篇文章,那麼JSP對你就是有用的,那麼在這裏來猜測下你的身份。

​可能一:學生、Web開發小白,在初學時進行簡單的項目開發時,往往所有的工作都是一個人完成的,如果採用前後端分離,那麼json、ajax、jQuery、Vue、Rest等等技術,都是你可能需要直面的問題,那麼可能真的要走一條從入門到放棄的道路了,而JSP則可以幫我們將上述工作進行簡化,所有的事全包在JSP身上了,讓你可以把更多的精力專於一點,而不是被繁雜的技術嚇到。

​可能二:開發人員、在維護一個老項目,JSP與ASP在上世紀90年代末與本世紀前十年,一直程序員進行Web開發的首選,並且其宿主語言Java最後還乾死了C#,所以許多早期的項目都是使用JSP開發的,並且看不到重構的希望,那麼你也只能繼續使用JSP來繼續進行功能的開發或者bug修復了。

​可能三:吃瓜羣衆。

資源分配圖

​閒話聊完了,下面我們就來學下JSP的相關知識。

1.什麼是JSP

​在Web開發中,當我們訪問Servlet,不可避免的要對用戶的請求進行響應,在前面我們知道,可以通過response向前端輸出html頁面,但是這樣,需要寫許多的輸出語句,並且許多靜態內容會和動態內容糅雜在一起,導致Servlet程序變得非常臃腫。爲此,Sun公司在Servlet的基礎上,推出了JSP技術。

資源分配圖

​那JSP到底是啥呢?JSP的全稱爲Java服務端頁面,英文全稱爲Java Server Pages,是一種以Servlet爲基礎的動態網頁開發技術。在JSP文件,HTML代碼嵌入Java代碼,網頁中的靜態內容主要由Html代碼來完成,Java代碼用來實現網頁中的動態內容的顯示。

​JSP技術其宿主語言是Java,因此其可以自動的繼承Java的特徵,主要如下:

  • 跨平臺:使用JSP的Web應用,也是可以跨平臺的,只要操作系統中安裝了JDK和Web服務器即可,JSP可以和Java同樣的實現,一次編譯多處運行;
  • **預編譯:**預編譯就是在用戶第一次通過瀏覽器訪問JSP頁面時,服務器將對JSP頁面代碼進行編譯,並且僅執行一次編譯。編譯好的代碼將被保存,在用戶下一次訪問時,會直接執行編譯好的代碼;
  • **組件重用:**JSP中可以引用Java代碼,即可以在JSP中直接引用我們寫的Service層的方法等等,不過這裏不建議如此操作。

2.一個JSP頁面包含的內容

​創建一個JSP頁面很簡單,我們這裏就不在贅述了。那一個JSP頁面,應該包含哪些內容呢?

資源分配圖
  • JSP指令:比如頁面第一行的page指令,其可以對本JSP頁面上的某些特性進行描述,如指明語言、頁面編碼方式、MIME類型等,當然還可以指定頁面的其他屬性;
  • HTML:JSP頁面中完全兼容HTML,包括js文件、css文件的引入,script標籤等;
  • Java腳本:也稱爲JSP腳本,即在JSP頁面中嵌入Java代碼,主要有三種形式,<% ... %>、<%! ... %>、<%= ... %>
  • JS腳本:這本身也是HTML中的用法,這裏提出來就是爲了和上面的Java腳本做一個區別,JSP頁面中的許多交互,比如點擊事件、alert等操作,仍然是有JS來完成的;
  • 九大內置對象:也稱之爲隱式對象,在JSP頁面中,我們可以很方便的使用一些對象,如out、request、session等。

3.JSP運行原理

​我們在之前的一篇文章中,講到缺省的urlPattern時,不知道還記得Tomcat中conf目錄下web.xml中的一段代碼麼?

<servlet>
  <servlet-name>jsp</servlet-name>
  <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
  <init-param>
    <param-name>fork</param-name>
    <param-value>false</param-value>
  </init-param>
  <init-param>
    <param-name>xpoweredBy</param-name>
    <param-value>false</param-value>
  </init-param>
  <load-on-startup>3</load-on-startup>
</servlet>
<!--  .....  -->
<servlet-mapping>
  <servlet-name>jsp</servlet-name>
  <url-pattern>*.jsp</url-pattern>
  <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

​我們可以看到,jsp頁面是有指定的Servlet來進行處理的,JspServlet可以理解爲JSP引擎(容器),其可以將一個JSP文件轉換成一個Servlet,我們可以理解爲比較特殊的"編譯"。

資源分配圖

​通過上圖,我們來對JSP的運行過程做更具體的描述:

  1. 客戶端發出請求,訪問JSP頁面資源;
  2. 如果頁面是第一次被訪問,JSP容器會將JSP文件轉換成一個Java文件,即一個Java Servlet程序,如果轉換過程發生異常,則終止,並拋出異常;
  3. 如果轉換成功,則由Servlet容器將其加載至內存(執行init方法),並根據urlPattern來進行調用(調用service方法),處理用戶對JSP頁面的請求;
  4. 如果JSP頁面在項目運行過程中被修改了,則服務器會根據設置決定是否進行重新編譯(默認重新編譯),如果需要重新編譯,則將新生成的Servlet加載入內存,這也是爲什麼我們在eclipse中開發時,JSP文件修改後不需要重啓項目(idea中需要自行設置);
  5. 當用戶的JSP請求被對應JSP的Servlet處理完成後,響應由對應的JSP容器進行接收,並將HTML格式的響應信息發送回客戶端。

​下面我們通過一個例子來講解下上面的流程,首先我們啓動項目,我們可以先行查看下Tomcat安裝目錄/work/Catalina/localhost/FirstProject/org/apache/jsp文件夾下,其中爲空,沒有任何文件,當我們在瀏覽器中訪問jspDemo.jsp後,我們可以看到,此目錄中多瞭如下兩個文件:

資源分配圖

​其中的jspDemo_jsp.java即爲JSP容器將jspDemo.jsp轉換得到的,其中的部分代碼如下:

public final class jspDemo_jsp extends org.apache.jasper.runtime.HttpJspBase
  implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
  //...
  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=UTF-8");
      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;
			//將jsp中的html進行輸出
      out.write("\n");
      out.write("<!DOCTYPE html>\n");
      out.write("<html>\n");
      out.write("<head>\n");
      out.write("<meta charset=\"UTF-8\">\n");
      out.write("<title>Insert title here</title>\n");
      out.write("<script src=\"./js/jquery-3.2.1.min.js\"></script>\n");
      out.write("</head>\n");
      out.write("<body>\n");
      out.write("\t<h1>這是HTML標籤</h1>\n");
      out.write("\t");
      
 			//java語句直接嵌入
      out.print("<h1>這是Java輸出的標籤</h1>");
      //註釋
      if(true){
        out.print("<h1>還可嵌入Java語句</h1>");
      }
	
      out.write("\n");
      out.write("\t\n");
      out.write("\t<script type=\"text/javascript\">\n");
      out.write("\t\t$(document).ready(function(){\n");
      out.write("\t\t\tconsole.log(\"頁面中同樣可以嵌入JS腳本\");\n");
      out.write("\t\t})\n");
      out.write("\t</script>\n");
      out.write("</body>\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      //...
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }            
}

​從上面編譯後得到的jspDemo_jsp.java可以看到,我們爲何能直接在jsp頁面中使用out對象,就是因爲JSP會幫我們定義這些內置對象。

​對於jspDemo_jsp繼承的org.apache.jasper.runtime.HttpJspBase,我們來看下其聲明(Jasper Runtime jar包中),可以看到,其實HttpServlet的一個子類,也證實了JSP頁面會被JSP引擎轉換成一個Servlet。

public abstract class HttpJspBase extends HttpServlet implements HttpJspPage{
  //...
}

4.小結

​JSP雖然在慢慢的退出Web開發的舞臺,但是其留下的風景還是非常讓人迷醉的,而且其更適合新手學習,所以在短期內,Java Web的學習中,JSP仍然是很重要的一塊。本文僅是簡單的介紹了JSP的概念,和JSP頁面中可以包含的內容,後續我們會對JSP進行詳細的講解。


​又到了分隔線以下,本文到此就結束了,本文內容全部都是由博主自己進行整理並結合自身的理解進行總結,如果有什麼錯誤,還請批評指正。

​Java web這一專欄會是一個系列博客,喜歡的話可以持續關注,如果本文對你有所幫助,還請還請點贊、評論加關注。

​有任何疑問,可以評論區留言。

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