Tomcat 与 Java Web开发技术详解(第三版)第六章 JSP技术 笔记

本文详细介绍了JSP技术,对比了静态HTML文件、Servlet和JSP生成HTML的方式。JSP通过在HTML中嵌入Java代码,结合指令、声明、程序片段和表达式等语法,实现动态网页。JSP生命周期包括解析、翻译、编译、初始化和运行阶段,其本质是Servlet。此外,文章还讨论了请求转发、包含、异常处理和预编译JSP等内容,强调了JSP的灵活性和效率。
摘要由CSDN通过智能技术生成

第六章 JSP技术

6.1 比较 HTML、Servlet 和 JSP

6.1.1 静态 HTML 文件

对于hello.html文件,存在于Web应用的文件系统中,当客户端请求访问时,Web服务器会读取文件系统中的hello.html文件,把它作文响应正文发送给浏览器。所以,每次客户访问该文件,客户得到的时同样的内容。

6.1.2 用Servelt动态生成 HTML 文件

public class HelloServlet extends HttpServlet {
  /** 响应客户请求*/
  public void doGet(HttpServletRequest request,HttpServletResponse response)
    throws ServletException, IOException {

    //获得username请求参数 
    String username=request.getParameter("username");
  
    /*输出HTML文档*/
    PrintWriter out = response.getWriter();
    out.println("<html><head><title>helloApp</TITLE></head>");
    out.println("<body>");
    out.println("<b>Hello,"+username+"</b>");
    out.println("</body></html>");
     
    out.close(); //关闭PrintWriter

  }
}

6.1.3 用 JSP 动态生成 HTML 文档

在传统的HTML文件中加入java程序片段和JSP标记,就构成了JSP文件。
hello.jsp

<html>
<head>
  <title>helloapp</title>
</head>
<body>
  <b>Hello,<%= request.getParameter("username") %></b>
</body>
</html>

<%= request.getParameter(“username”) %> 采用了JSP语法,输出request.getParameter(“username”) 的返回值。
当浏览器访问hello.jsp时,按照以下流程来处理客户请求:

  1. 查找与 JSP 文件对应的Servlet,如果存在,就调用它的服务方法。
  2. 如果与JSP对应额Servlet不存在,就解析文件系统中的JSP文件,把它翻译成Servlet源文件,接着把Servlet源文件编译为 Servlet类,然后再初始化并运行Servlet。
    在这里插入图片描述
    Tomcat把由JSP生成的Servlet源文件和类放于<CATALINA_HONME>/work目录下。

hello.jsp.java

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.apache.jsp;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.el.ExpressionFactory;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspFactory;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import org.apache.jasper.runtime.HttpJspBase;
import org.apache.jasper.runtime.InstanceManagerFactory;
import org.apache.jasper.runtime.JspSourceDependent;
import org.apache.jasper.runtime.JspSourceImports;
import org.apache.tomcat.InstanceManager;

public final class hello_jsp extends HttpJspBase implements JspSourceDependent, JspSourceImports {
    private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
    private static Map<String, Long> _jspx_dependants;
    private static final Set<String> _jspx_imports_packages = new HashSet();
    private static final Set<String> _jspx_imports_classes;
    private volatile ExpressionFactory _el_expressionfactory;
    private volatile InstanceManager _jsp_instancemanager;

    static {
        _jspx_imports_packages.add("javax.servlet");
        _jspx_imports_packages.add("javax.servlet.http");
        _jspx_imports_packages.add("javax.servlet.jsp");
        _jspx_imports_classes = null;
    }

    public hello_jsp() {
    }

    public Map<String, Long> getDependants() {
        return _jspx_dependants;
    }

    public Set<String> getPackageImports() {
        return _jspx_imports_packages;
    }

    public Set<String> getClassImports() {
        return _jspx_imports_classes;
    }

    public ExpressionFactory _jsp_getExpressionFactory() {
        if (this._el_expressionfactory == null) {
            synchronized(this) {
                if (this._el_expressionfactory == null) {
                    this._el_expressionfactory = _jspxFactory.getJspApplicationContext(this.getServletConfig().getServletContext()).getExpressionFactory();
                }
            }
        }

        return this._el_expressionfactory;
    }

    public InstanceManager _jsp_getInstanceManager() {
        if (this._jsp_instancemanager == null) {
            synchronized(this) {
                if (this._jsp_instancemanager == null) {
                    this._jsp_instancemanager = InstanceManagerFactory.getInstanceManager(this.getServletConfig());
                }
            }
        }

        return this._jsp_instancemanager;
    }

    public void _jspInit() {
    }

    public void _jspDestroy() {
    }

    public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        if (!DispatcherType.ERROR.equals(request.getDispatcherType())) {
            String _jspx_method = request.getMethod();
            if ("OPTIONS".equals(_jspx_method)) {
                response.setHeader("Allow", "GET, HEAD, POST, OPTIONS");
                return;
            }

            if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
                response.setHeader("Allow", "GET, HEAD, POST, OPTIONS");
                response.sendError(405, "JSPs only permit GET, POST or HEAD. Jasper also permits OPTIONS");
                return;
            }
        }

        JspWriter out = null;
        JspWriter _jspx_out = null;
        PageContext _jspx_page_context = null;

        try {
            response.setContentType("text/html");
            PageContext pageContext = _jspxFactory.getPageContext(this, request, response, (String)null, true, 8192, true);
            _jspx_page_context = pageContext;
            pageContext.getServletContext();
            pageContext.getServletConfig();
            pageContext.getSession();
            out = pageContext.getOut();
            out.write("<html>\r\n");
            out.write("<head>\r\n");
            out.write("  <title>helloapp</title>\r\n");
            out.write("</head>\r\n");
            out.write("<body>\r\n");
            out.write("  <b>Hello,");
            out.print(request.getParameter("username"));
            out.write("</b>\r\n");
            out.write("</body>\r\n");
            out.write("</html>\r\n");
        } catch (Throwable var13) {
            if (!(var13 instanceof SkipPageException)) {
                out = (JspWriter)_jspx_out;
                if (_jspx_out != null && ((JspWriter)_jspx_out).getBufferSize() != 0) {
                    try {
                        if (response.isCommitted()) {
                            out.flush();
                        } else {
                            out.clearBuffer();
                        }
                    } catch (IOException var12) {
                    }
                }

                if (_jspx_page_context == null) {
                    throw new ServletException(var13);
                }

                _jspx_page_context.handlePageException(var13);
            }
        } finally {
            _jspxFactory.releasePageContext(_jspx_page_context);
        }

    }
}

从以上代码可以看出,与JSP对应的Servlet类org.apache.jasper.runtime.HttpJspBase类,HttpJspBase类由Tocmat提供,HttpJspBase类实现了JSP API 中的HttpJspPage接口,该接口继承了javax.servlet.jsp.JspPage接口,而JspPage接口继承了Servlet API中的javax.servlet.Servlet接口。
在这里插入图片描述
hello.jsp中的HTML 文本称为模板文本,它会被原封不动的发送个客户端。以下代码用于输出模板文件。

	out.write("<html>\r\n");
    out.write("<head>\r\n");
    out.write("  <title>helloapp</title>\r\n");
    out.write("</head>\r\n");
    out.write("<body>\r\n");
    out.write("  <b>Hello,");
    out.print(request.getParameter("username")); 
    out.write("</b>\r\n");
    out.write("</body>\r\n");
    out.write("</html>\r\n");

out.print(request.getParameter(“username”)); 与jsp文件中的<%=request.getParameter(“username”)%> 对应。
从上述可以看出,JSP形式上是HTML文件,但是本质上是Servlet。所以Servlet的特性都适用于JSP。

6.2 JSP语法

6.2.1 JSP指令(Directive)

JSP指令(<%@ 和%>内)用来设置和整个JSP网页相关的属性,如网页的编码方式和脚本语言等。

<%@ 指令名 属性="值"%>

常用的三种指令为page、include和taglib。

1. page 指令
page指令可以指定所使用的编程语言、与JSP对应的Servlet所实现的接口、所扩展的类以及导入的软件包等。语法形式如下:

<%@ page 属性1="值1" 属性2="值2" %>

page指令的属性表

page指令的属性                描述        举例
language指定程序代码所使用的编程语言,目前仅java为有效值和默认值<%@ page lanuage=“java”%>
method指定java程序片段(SDcriptlet)所属的方法名称java程序片段会成为指定方法的主体。默认方法时serivce(),当多次使用该指令时只有第一次是有效的。有效值包括service,doGet,doPost等<%@ page method=“doPost”%>
import导入指定的java软件包,可以使用多次,可以用逗哈分隔<%@ page import=“java.io.*,java.util.Hshtable”%>
content_type指定响应结果的MIME类型,默认是text/html,默认编码是ISO-8859-1<%@ page content_type=“text/html;chaset=GBK”%>
session=“true/false”指定jsp是否使用session,默认true<%@ page session=“true”
errorPage=“error_url”指定当发生异常时,客户请求转发到哪个网页<%@ page errorPage=“error.jsp”%>

2. include指令

JSP通过include指令来包含其他文件的内容,被包含的文件可以是JSP或HTML文件。语法为:

	<%@ include file="目标组件的绝对URL或相对URL" %>

把多数网页都需要的相同的内容放在一个文件中,其他的JSP文件通过 include 指令来将这个文件包含进来,提高代码的可重用性。
banner.jsp

	<body>
		<img src="logo.bmp">
	</body>

bookstore.jsp

	<@ page content_type="text/html;charset=GBK"%>
	<@ include file="common.jsp"%>
	<html>
		<head>
 			 <title>helloapp</title>
		</head>
		 <@ include file="banner.jsp"%>
	<body>
  		<b>Hello</b>
	</body>
</html>

用 include 指令来包含其他文件被看做时静态包含。

6.2.2 JSP 声明

JSP声明(<%! 和 %>内)用于声明JSP对应的Servlet类的成员变量和方法。语法如下

<%! declaration;[frvilststion;]....%>

例如:

	<%! int v1=0;%>
	<%! int v2,v3,v4;%>
	<%! String v5="hello"; static int v6;%>
	<%!
		public String amethod(int i){
			if (i < 3){
				return "i<3";
			} else {
				return "i>=3";
			}
		}
	%>

6.2.3 java 程序片段(Scriptlet)

在JSP文件中,可以在 "<% 和 %>"标记间直接嵌入任何有效的java程序代码。这种嵌入的程序片段成为Scriptlet。如果在page指令中没有指定method属性,那么这些代码默认是属于JSP对应的Servlet类的service()方法中的代码块。

	<%
		String gender="female";//局部变量
		if(gender.equals("female")) {
	%>
	she is a girl. <%-- 模板文本 --%>
	<% } else { %>
	He is a boy
	<%  }   %>

等价于Servlet service()方法中的:

	String gender = "female";
	if (gender.equals("female")) {
		out.print("she is a girl");
	} else {
		out.print("He is a boy");
	}

6.2.4 Java 表达式

java表达式的标记为 <%= %> ,如果在JSP文件的模板文本中使用该标记,那么它能把表达式的值输出到网页上。

6.2.5 隐含对象

JSP中的隐含对象

    隐含对象的引用变量      隐含对象的类型
 request javax.servelt.HttpServletRequest
 response javax.servlet.HttpServletResponse
 pageContext javax.servlet.jsp.PageContext
 application java.servlet.ServletContext
 out java.servlet.jsp.JspWriter
 config java.servlet.ServletConfig
 page java.lang.Object(相当于Java中的this关键字)
 session javax.servlet.http.HttpSession
 exception java.lang.Exception

6.3 JSP 的生命周期

JSP 与 Servlet的区别在于,Servlet容器必须先把JSP编译成 Servlt 类,然后才允许它。

  • 解析阶段:Servlet容器解析JSP文件的代码,如果有语法错误,就会想客户端返回错误信息。
  • 翻译阶段:Servlet容器将 JSP 文件翻译成 Servlet 源文件。
  • 编译阶段:Servlet 容器编译 Servlet源文件,生成 Servlet类。
  • 初始化阶段:加载与 JSP 文件对应的 Servlet类,创建实例。
  • 运行阶段:调用Servlet实例的方法。
  • 销毁阶段:调用与 JSP 对应的 Servlet的销毁方法。

解析、翻译、编译阶段是JSP生命周期中独有的,这三个阶段发生在一下场合:

  • JSP 文件首次被访问。
  • JSP 文件被更新。
  • JSP 文件对应的 Servelt 类被手动删除。

JSP 对应的Servlet类实现了 javax.servlet.jsp.JspPage接口,JspPage继承了javax.servlet.Servlet接口。JspPage接口中定义了jspInit()方法和jspDestroy()方法,与Servlet中的init()和destory对应。在编写JSP文件时,可以实现jspInit()方法和jspDestory()方法。

	<%! 
		public void jspInit(){
			//初始换操作
		}
	%>
	<%! 
		public void jspDestory(){
			//销毁操作
		}
	%>

visit.jsp

<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.io.*" %>
<html><head><title>visit.jsp</title></head><body>

<%!
  File tempDir=null; //实例变量
  
  public void jspInit(){
    ServletContext application=getServletConfig().getServletContext();
    tempDir=(File)application.getAttribute("javax.servlet.context.tempdir");
  }
%>

工作目录为:<%=tempDir.getPath() %>

</body></html>

对于visit.jsp,Servlet容器在编译时,会产生错误编译,因为application变量是在JSP的_jspService()方法中定义的局部变量,在其他任何地方都是无法访问的。可以通过getServletConfig().getServletContext()方法来获取ServletContxt对象。

6.4 请求转发

JSP采用 <jsp:forward> 标签来进行请求转发。

<jsp:forwrd page="转发的目标组件的绝对地址或相对地址">

JSP源组件的<jsp:forward>标签以后的代码不会被执行。

<jsp:param>标签来向转发的目标组件传递额外的请求参数。

<html>
	<head><title>source組件</title></head>
	<body>
		<jsp:forwrad page="target.jsp">
			<jsp:param name="username" value="Tom" />
			<jsp:param name="password" value="123456" />
		</jsp:forward>
	</body>
</html>

在target.jsp中可以通过request.getparameter(“username”)来获取参数。

6.5 包含

用include指令来包含,静态包含

<%@ include file="被包含组件的绝对路径或相对路径"%>

用include标签包含,动态包含

<jsp:include page="被包含组件的绝对路径或相对路径">

6.5.1 静态包含

sin.jsp

sin.jsp is including content.jsp.
<% int var=1; 
   request.setAttribute("username","Tom");
%>
<%@ include file="content.jsp" %> 

<p>sin.jsp is doing something else.

content.jsp

<p>
Output from content.jsp:
<br>
var=<%=var %> 
<br>
username=<%=request.getAttribute("username") %>

当请求访问sin.jsp时,Tomcat按以下流程响应客户端:

  1. 解析sin.jsp,在解析 <%@ include file=“content.jsp” %> 时,把content.jsp的所有资源融合到sin.jsp中。融合后的代码如下:
sin.jsp is including content.jsp.
<% int var=1; 
   request.setAttribute("username","Tom");
%>

<%--content.jsp开始 --%>
<p>
Output from content.jsp:
<br>
var=<%=var %> 
<br>
username=<%=request.getAttribute("username") %>
<%--content.jsp结束 --%>

<p>sin.jsp is doing something else.
  1. 把融合后的源码翻译为Servlet源文件,再编译为Servlet类。
  2. 初始化与sin.jsp对应的Servlet,再运行它的服务方法。

得出,静态包含是发生在解析JSP源组件阶段,被包含的目标文件中的内容被原封不动的添加到了JSP源组件中,Servlet容器再对JSP源组件进行翻译和编译。静态包含可以为HTML或JSP文件,单不能为Servlet。如果为JSP文件,那么被包含文件可以访问源组件中定义的局部变量,因为实际上,JSP 源组件和被包含组件对应的是同一个Servlet。

6.5.2 动态包含

din.jsp

din.jsp is including content.jsp.
<% int var=1;
    request.setAttribute("username","Tom");
%>
<jsp:include page="content.jsp" />

content.jsp

<p>
Output from content.jsp:
<br>
var=<%=var %> 
<br>
username=<%=request.getAttribute("username") %>

访问din.jsp时会报编译错误

org.apache.jasper.JasperException: Unable to compile class for JSP: 

An error occurred at line: [4] in the jsp file: [/content.jsp]
var cannot be resolved to a variable
1: <p>
2: Output from content.jsp:
3: <br>
4: var=<%=var %> 
5: <br>
6: username=<%=request.getAttribute("username") %>

原因:content.jsp无法识别局部变量var,因为这是动态包含,目标组件和源组件是不同的Servlet。两个Servlet之间是无法访问对方的服务方法中的局部变量的。

对于动态包含,Tomcat按以下流程响应客户端请求。

  1. 解析din.jsp(源)并翻译为Servlet源文件(.java),din.jsp中的<jsp:include page="content.jsp"/> 被翻译为如下代码:
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "content.jsp", out, false);
  1. 把Servlet源文件编译为Servlet类(.class)。
  2. 初始化din.jsp对应的Servlet,再运行它的服务方法。
  3. 与din.jsp对应的Servlet的服务方法会调用org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, “content.jsp”, out, false)方法,当执行这个方法时,会解析content.jsp,如果没有语法错误,就翻译为Servelt源文件,再编译为Servlet类,运行服务方法。
  4. Servlet容器执行完JspRuntimeLibrary.include(request, response, “content.jsp”, out, false)方法后,继续执行din.jsp代表的Servlet的服务方法的后续代码。

<jsp:include />标签有一个flush属性,值为 true 或 false,true,表示源组件在包含目标组件之前就把响应正文提交给了客户端。

6.5.3 混合使用动态包含和静态包含

静态包含通常用来包含不会发生变化的网页内容,二动态包含通常用来包含会发生变化的网页内容。

6.6 JSP异常处理

JSP在发生异常时,可以通过以下指令将请求转发给另一个专门处理异常的页面:

	<%@ page errorPage="errorpage.jsp"%>

<%@ page isErrorPage=“true”%> 指令将网页声明为异常处理页面。

抛出异常的网页和处理异常的网页之间是请求转发关系,所以,处理异常的网页可以直接访问exception隐含对象获取当前异常的详细信息。

<p>
	错误原因:<%exception.printStackTrace(new PrinterWriter(out));%>
</p>

6.7 发布JSP

将JSP文件放到应用的根目录下即可。或者通过web.xml配置

  <servlet>
    <servlet-name>hi</servlet-name>
    <jsp-file>/hello.jsp</jsp-file>
  </servlet>

  <servlet-mapping>
    <servlet-name>hi</servlet-name>
    <url-pattern>/hi</url-pattern>
  </servlet-mapping>

6.8 预编译JSP

当JSP文件被客户端第一次请求访问时,Servlet容器需要先把JSP文件编译为Sevlet类才能运行,这一过程会延长客户的等待时间,所以,可以对JSP文件进行预编译。

6.9 PageContext类的用法

JSP文件中使用PageContext类的场合主要有:

  • JSP文件中的Java片段。
  • JS批文件中的自定义标签的处理类。

PageContext类中的方法可分为以下几种:

  1. 用于向各种范围内存取属性的方法
	// 返回页面范围内特定属性的值
	Object getAttribute(String name);
	//返回参数scope指定范围内的特定的值
    Object getAttribute(String name, int scope);
    // 向参数scope指定的范围内存放属性
	void setAttribute(String name, Object value, int scope);
	// 删除scope指定范围内的特定属性
	void removeAttribute(String name,int scope);
	// 依次从页面范围、请求范围、会话范围和web应用范围内查找name指定的属性,如果找到,就立即返回,如果所有范围都不存在,则返回null
	Object findAttribute(String name); 
	// 返回参数指定的属性的所属范围
	int getAttributesScope(String name);

以上scope的取值为 PgaeContext的四个静态常量

 	public static final int PAGE_SCOPE = 1; //页面
    public static final int REQUEST_SCOPE = 2; //请求
    public static final int SESSION_SCOPE = 3; //会话
    public static final int APPLICATION_SCOPE = 4; //web应用

PageContext对象是由容器创建的,所以JSP文件可以直接通过固定变量 pageContext来因哟呵你给隐含的PageContext对象。

  1. 用于获得由Servlet容器提供的其他对象的引用的方法。
	// 返回当前JSP对应的Servlet实例
	Object getPage();
	
	ServletRequest getRequest();
	ServletResponse getResponse();
	ServletConfig getServletConfig();
	ServletContext getServletContext();
	HttpSession getSession();
	//返回一个用于输出响应正文的jspWriter对象
	JspWriter getOut();

在JSP文件的Java程序片段中可以直接通过该application、request等固定变量来引用PageContext、ServeltReqeust和ServeletResponse等对象,而在自定义的JSP标签中时不能使用的,此时就需要依靠pageContext对象来引用这些对象。

  1. 用于请求转发和包含的方法。
void forward(String relativeUrlPath) throws ServletException, IOException;
void include(String relativeUrlPath) throws ServletException, IOException;

6.10 在 web.xml 中配置JSP

 <jsp-config>
    <jsp-property-group>
      <!--对JSP进行描述-->
      <description>special property grouup for JSP Configurationn</description>
      <!-- 设定改配置所影响的jsp,这里表示对所有以.jsp结尾的文件有效 -->
      <url-pattern>*.jsp</url-pattern>
      <!-- 为true表示不支持EL表达式-->
      <el-ignored>true</el-ignored>
      <page-encoding>GBK</page-encoding>
      <!--为true表示不支持<%%>java程序片段-->
      <scripting-invalid>true</scripting-invalid>
    </jsp-property-group>

    <jsp-property-group>
      <!--对JSP进行描述-->
      <description>special property grouup for JSP Configurationn</description>
      <!-- 设定改配置所影响的jsp,这里表示对web引用下mypath目录中的文件有效 -->
      <url-pattern>/mypath</url-pattern>
      <!-- 为true表示不支持EL表达式-->
      <el-ignored>true</el-ignored>
      <!--自动包含jsp页面的头文件-->
     <include-prelude>/include/head.jsp</include-prelude>
      <!-- 自动包含JSP页面的尾部文件-->
      <include-coda>/include/food.jsp</include-coda>
    </jsp-property-group>
    
  </jsp-config>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值