Jsp鞏固複習——原理

1. JSP簡介

JSP :Java Server Page 縮寫,由sun公司倡導,許多公司參與,於1999年推出的一種Web服務設計標準。

JSP基於java servlet 以及整個java體系的web開發技術,可以建立安全跨平臺先進動態網站

與ASP比較
特點:java技術支持,動態頁面與靜態頁面分離,跨平臺,編譯後運行

B/S模式:
用戶使用瀏覽器向服務器發出請求,服務器對此作出響應,將有關信息發送到用戶的瀏覽器。

1.1 配置JSP環境

(1)安裝JDK,設置環境變量
(2)安裝Tomcat

1.2 JSP頁面

在HTML文件頭部加上:<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<%,%>之間寫java程序片段

1.3 web服務目錄

網站,實際上是一個web服務目錄

(1)根目錄:D:\tomcat 9.0\webapps\Root

(2)web服務目錄:
webapps下的任何一個子目錄都可以作爲一個web服務目錄。
D:\tomcat 9.0\webapps\Ch1 --> http://172.0.0.1:8080/Ch1/test.jsp

(3) 新建web服務目錄
在非webapps目錄下創建目錄,將該目錄設置虛擬目錄。
通過虛擬目錄訪問Web服務目錄中的jsp頁面。

方式:修改Tomcat安裝目錄下的conf文件夾中的server.xml。

</Host>前面加入:

<Content path="/Virtual" docBase="D:MyWeb\Ch1" debug="0" reloadable="true" />

path:指定訪問該Web應用的URL入口。
docBase:指定Web應用的文件路徑,可以給定絕對路徑,也可以給定相對於<Host>的appBase屬性的相對路徑,如果Web應用採用開放目錄結構,則指定Web應用的根目錄,如果Web應用是個war文件,則指定war文件的路徑。
reloadable:如果這個屬性設爲true,tomcat服務器在運行狀態下會監視在WEB-INF/classes和WEB-INF/lib目錄下class文件的改動,如果監測到有class文件被更新的,服務器會自動重新加載Web應用。

1.4 JSP運行原理

當服務器上的一個JSP頁面被第一次請求執行時,服務器上的JSP引擎首先將JSP頁面文件轉譯成一個java文件,並編譯這個java文件生成的字節碼文件,然後執行字節碼文件響應客戶的請求。

當這個JSP頁面再次被請求執行時,JSP引擎將直接執行字節碼文件來響應客戶,這是比ASP速度快的一個原因。

字節碼文件主要工作:
(1)把JSP頁面中的HTML標記符號(頁面的靜態部分)交給客戶的瀏覽器負責顯示
(2)負責處理JSP標記,並將有關的處理結果發送到客戶的瀏覽器
(3)執行<% %>之間的java程序片段,把執行結果交給客戶的瀏覽器顯示
(4)當多個客戶請求一個JSP頁面時,Tomcat服務器爲每個客戶啓動一個線程,該線程負責執行常駐內存的字節碼文件來響應相應客戶的請求。
(這些線程由Tomcat服務器負責管理,將CPU的使用權在各個線程之間快速切換,以保證每個線程都有機會執行字節碼文件)

1.4.1 Web服務器是如何調用並執行一個jsp頁面的?

瀏覽器向服務器發請求,不管訪問的是什麼資源,其實都是在訪問Servlet,所以當訪問一個jsp頁面時,其實也是在訪問一個Servlet,服務器在執行jsp的時候,首先把jsp翻譯成一個Servlet,所以我們訪問jsp時,其實不是在訪問jsp,而是在訪問jsp翻譯過後的那個Servlet,例如下面的代碼:

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>First Jsp</title>
    
  </head>
  
  <body>
    <%
        out.print("Hello Jsp");
    %>
  </body>
</html>

當我們通過瀏覽器訪問index.jsp時,服務器首先將index.jsp翻譯成一個index_jsp.class,在Tomcat服務器的work\Catalina\localhost\項目名\org\apache\jsp目錄下可以看到index_jsp.class的源代碼文件index_jsp.java,index_jsp.java的代碼如下:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

  private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

  private static java.util.List _jspx_dependants;

  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  public Object getDependants() {
    return _jspx_dependants;
  }

  public void _jspInit() {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
  }

  public void _jspDestroy() {
  }

  public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
    JspWriter _jspx_out = null;
    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;

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

String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

      out.write("\r\n");
      out.write("\r\n");
      out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
      out.write("<html>\r\n");
      out.write("  <head>\r\n");
      out.write("    <base href=\"");
      out.print(basePath);
      out.write("\">\r\n");
      out.write("    \r\n");
      out.write("    <title>First Jsp</title>\r\n");
      out.write("\t\r\n");
      out.write("  </head>\r\n");
      out.write("  \r\n");
      out.write("  <body>\r\n");
      out.write("    ");

        out.print("Hello Jsp");
    
      out.write("\r\n");
      out.write("  </body>\r\n");
      out.write("</html>\r\n");
    } catch (Throwable t) {
      if (!(t instanceof 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);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

我們可以看到,index_jsp這個類是繼承 org.apache.jasper.runtime.HttpJspBase這個類的,通過查看Tomcat服務器的源代碼,可以知道在apache-tomcat-6.0.20-src\java\org\apache\jasper\runtime目錄下存HttpJspBase這個類的源代碼文件,如下圖所示:
在這裏插入圖片描述
我們可以看看HttpJsBase這個類的源代碼,如下所示:

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jasper.runtime;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.HttpJspPage;
import javax.servlet.jsp.JspFactory;

import org.apache.jasper.compiler.Localizer;

/**
 * This is the super class of all JSP-generated servlets.
 *
 * @author Anil K. Vijendran
 */
public abstract class HttpJspBase  extends HttpServlet  implements HttpJspPage {
    
    protected HttpJspBase() {
    }

    public final void init(ServletConfig config) 
    throws ServletException 
    {
        super.init(config);
    jspInit();
        _jspInit();
    }
    
    public String getServletInfo() {
    return Localizer.getMessage("jsp.engine.info");
    }

    public final void destroy() {
    jspDestroy();
    _jspDestroy();
    }

    /**
     * Entry point into service.
     */
    public final void service(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException 
    {
        _jspService(request, response);
    }
    
    public void jspInit() {
    }

    public void _jspInit() {
    }

    public void jspDestroy() {
    }

    protected void _jspDestroy() {
    }

    public abstract void _jspService(HttpServletRequest request, 
                     HttpServletResponse response) 
    throws ServletException, IOException;
}

HttpJspBase類是繼承HttpServlet的,所以HttpJspBase類是一個Servlet,而index_jsp又是繼承HttpJspBase類的,所以index_jsp類也是一個Servlet,所以當瀏覽器訪問服務器上的index.jsp頁面時,其實就是在訪問index_jsp這個Servlet,index_jsp這個Servlet使用_jspService這個方法處理請求。

1.4.2 Jsp頁面中的html排版標籤是如何被髮送到客戶端的?

瀏覽器接收到的這些數據

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="http://localhost:8080/JavaWeb_Jsp_Study_20140603/">
    
    <title>First Jsp</title>
    
  </head>
  
  <body>
    Hello Jsp
  </body>
</html>

都是在_jspService方法中使用如下的代碼輸出給瀏覽器的:

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

String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

      out.write("\r\n");
      out.write("\r\n");
      out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
      out.write("<html>\r\n");
      out.write("  <head>\r\n");
      out.write("    <base href=\"");
      out.print(basePath);
      out.write("\">\r\n");
      out.write("    \r\n");
      out.write("    <title>First Jsp</title>\r\n");
      out.write("\t\r\n");
      out.write("  </head>\r\n");
      out.write("  \r\n");
      out.write("  <body>\r\n");
      out.write("    ");

        out.print("Hello Jsp");
    
      out.write("\r\n");
      out.write("  </body>\r\n");
      out.write("</html>\r\n");

在jsp中編寫的java代碼和html代碼都會被翻譯到 _jspService 方法中去,
在jsp中編寫的java代碼會原封不動地翻譯成java代碼,如<%out.print(“Hello Jsp”);%>直接翻譯成out.print(“Hello Jsp”);,
而HTML代碼則會翻譯成使用out.write("<html標籤>\r\n");的形式輸出到瀏覽器。
在jsp頁面中編寫的html排版標籤都是以out.write("<html標籤>\r\n");的形式輸出到瀏覽器,瀏覽器拿到html代碼後才能夠解析執行html代碼。

1.4.3 Jsp頁面中的java代碼 服務器是如何執行的?

在jsp中編寫的java代碼會被翻譯到_jspService方法中去,當執行_jspService方法處理請求時,就會執行在jsp編寫的java代碼了,所以Jsp頁面中的java代碼服務器是通過調用_jspService方法處理請求時執行的。

1.4.4 Web服務器在調用jsp時,會給jsp提供一些什麼java對象?

查看_jspService方法可以看到,Web服務器在調用jsp時,會給Jsp提供如下的8個java對象

PageContext pageContext;
HttpSession session;
ServletContext application;
ServletConfig config;
JspWriter out;
Object page = this;
HttpServletRequest request, 
HttpServletResponse response

其中page對象,request和response已經完成了實例化,而其它5個沒有實例化的對象通過下面的方式實例化

1 pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
2 application = pageContext.getServletContext();
3 config = pageContext.getServletConfig();
4 session = pageContext.getSession();
5 out = pageContext.getOut();

這8個java對象在Jsp頁面中是可以直接使用的,如下所示:

<%
        session.setAttribute("name", "session對象");//使用session對象,設置session對象的屬性
        out.print(session.getAttribute("name")+"<br/>");//獲取session對象的屬性
        pageContext.setAttribute("name", "pageContext對象");//使用pageContext對象,設置pageContext對象的屬性
        out.print(pageContext.getAttribute("name")+"<br/>");//獲取pageContext對象的屬性
        application.setAttribute("name", "application對象");//使用application對象,設置application對象的屬性
        out.print(application.getAttribute("name")+"<br/>");//獲取application對象的屬性
        out.print("Hello Jsp"+"<br/>");//使用out對象
        out.print("服務器調用index.jsp頁面時翻譯成的類的名字是:"+page.getClass()+"<br/>");//使用page對象
        out.print("處理請求的Servlet的名字是:"+config.getServletName()+"<br/>");//使用config對象
        out.print(response.getContentType()+"<br/>");//使用response對象
        out.print(request.getContextPath()+"<br/>");//使用request對象
%>
1.4.5 Jsp最佳實踐

Jsp最佳實踐就是jsp技術在開發中該怎麼去用。

不管是JSP還是Servlet,雖然都可以用於開發動態web資源。但由於這2門技術各自的特點,在長期的軟件實踐中,人們逐漸把servlet作爲web應用中的控制器組件來使用,而把JSP技術作爲數據顯示模板來使用。其原因爲,程序的數據通常要美化後再輸出:讓jsp既用java代碼產生動態數據,又做美化會導致頁面難以維護。讓servlet既產生數據,又在裏面嵌套html代碼美化數據,同樣也會導致程序可讀性差,難以維護。因此最好的辦法就是根據這兩門技術的特點,讓它們各自負責各的,servlet只負責響應請求產生數據,並把數據通過轉發技術帶給jsp,數據的顯示jsp來做。
  
在這裏插入圖片描述
第一次執行:

客戶端通過電腦連接服務器,因爲是請求是動態的,所以所有的請求交給WEB容器來處理
在容器中找到需要執行的*.jsp文件
之後*.jsp文件通過轉換變爲*.java文件
.java文件經過編譯後,形成.class文件
最終服務器要執行形成的*.class文件

第二次執行:

因爲已經存在了*.class文件,所以不在需要轉換和編譯的過程

修改後執行:

1.源文件已經被修改過了,所以需要重新轉換,重新編譯。
在這裏插入圖片描述

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