1、什么是JSP

JSP(Java Server Pages)java服务器页面。JSPServlet一样,也是Sun公司主导创建的一种动态web技术。

  • JSP有一个最大的特点:写JSP就像写HTML。
  • JSP与HTML的区别:
    • HTML只给用户提供静态的数据。
    • JSP页面中可以嵌入Java代码,为用户提供动态数据。

2、JSP运行原理(重点)

JSP文件的运行原理如下图所示:

JSP原理

2.1、JSP文件的运行过程:

  • 客户端(浏览器)访问JSP页面,如http://localhost:8080/Prj_test/ch02/HelloJSP.jsp
  • 服务器找到相应的JSP页面(xxx.jsp文件)。
  • 服务器将JSP文件转换成Servlet文件(xxx_jsp.java文件)。
  • 服务器将Servlet文件编译为class文件(xxx_jsp.class文件)。
  • 服务器将class文件加载到内存并执行。
  • 服务器将class文件执行后生成HTML代码发送给客户端,客户端(浏览器)根据响应的HTML代码进行显示。

2.2、JSP页面的生命周期

我们可以把**JSP页面的生命周期分成“转换(translation)”“执行(execution)”**两个阶段:

(1)转换阶段:JSP页面转换成Servlet类文件。

注:

  • IDEA项目中,Tomcat转换后的生成的xxx_jsp.java文件存放在:C:\Users\XXX\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat\XXX\work\Catalina\localhost\cs\org\apache\jsp目录下。
  • JSP 文件不是在服务器启动的时候转换成Servlet类的,而是在被客户端访问的时候才可能发生转换的。
  • 当客户端(浏览器)向服务器请求一个JSP页面时,服务器首先判断该JSP 文件是不是第一次访问或者被修改。如果是第一次访问或者JSP 文件被修改,那么web容器(Tomcat)会将JSP文件(xxx.jsp)转换成一个Java类(xxx_jsp.java文件)。如果不是第一次访问且JSP文件内容没有被修改,那么就不再发生Servlet转换。

(2)执行阶段:Servlet类执行,将响应结果发送至客户端。

2.3、xxx_jsp.java文件源码分析

示例:xxx.jsp文件及其转换生成的xxx_jsp.java文件如下:

xxx.jsp文件

1
2
3
4
5
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>

xxx_jsp.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package org.apache.jsp;

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

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

private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();

……(此处省略部分代码)

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 {

//判断请求方式
if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
}


//定义9大内置对象
final javax.servlet.jsp.PageContext pageContext; //pageContext(页面上下文)
javax.servlet.http.HttpSession session = null; //session(会话)
final javax.servlet.ServletContext application; //application(应用上下文,本质就是ServletContext)
final javax.servlet.ServletConfig config; //config(配置,本质就是ServletConfig)
javax.servlet.jsp.JspWriter out = null; //out(输出对象)
final java.lang.Object page = this; //page(当前页面)
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;


try {
//内置对象初始化
response.setContentType("text/html");
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;

//打印输出HTML页面
out.write("<html>\n");
out.write("<body>\n");
out.write("<h2>Hello World!</h2>\n");
out.write("</body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
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);
}
}
}

查看转换生成的xxx_jsp.java文件源码会发现:

2.3.1、JSP文件的本质

该转换后的这个类继承了HttpJspBase类,而HttpJspBase类也继承了HttpServlet类,因此:JSP文件本质上也是一个Servlet。

转换类 转换类父类

**注:**分析xxx_jsp.java文件源码需要在项目的pom.xml文件中导入jasper-runtime依赖包。

1
2
3
4
5
<dependency>
<groupId>tomcat</groupId>
<artifactId>jasper-runtime</artifactId>
<version>5.5.23</version>
</dependency>

2.3.2、核心方法:_jspService详解

xxx_jsp.java文件中也重写了父类(HttpJspBase类)的public abstract void _jspService(HttpServletRequest var1, HttpServletResponse var2)方法。

**注:**该方法类似于HttpServlet类中的核心方法:service(HttpServletRequest req, HttpServletResponse resp)

_jspService方法中主要完成以下功能:

(1)判断请求方式

1
2
3
4
5
6
7
8
9
10
11
12
13
//判断请求方式
if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
}

(2)定义9大内置对象(重点)

1
2
3
4
5
6
7
8
9
10
//定义9大内置对象
final javax.servlet.http.HttpServletRequest request;//request(请求)
final javax.servlet.http.HttpServletResponse response;//response(响应)
final javax.servlet.jsp.PageContext pageContext; //pageContext(页面上下文)
javax.servlet.http.HttpSession session = null; //session(会话)
final javax.servlet.ServletContext application; //application(应用上下文,本质就是ServletContext)
final javax.servlet.ServletConfig config; //config(配置,本质就是ServletConfig)
javax.servlet.jsp.JspWriter out = null; //out(输出对象)
final java.lang.Object page = this; //page(当前页面)
//还差一个内置对象:exception

注:9大内置对象可以在JSP页面中直接使用。

(3)内置对象初始化

1
2
3
4
5
6
7
8
9
10
//内置对象初始化
response.setContentType("text/html");
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;

(4)打印输出HTML页面

1
2
3
4
5
6
//打印输出HTML页面
out.write("<html>\n");
out.write("<body>\n");
out.write("<h2>Hello World!</h2>\n");
out.write("</body>\n");
out.write("</html>\n");

**结论:**在JSP页面中,只要是Java代码就会被原封不动的输出;如果是HTML代码,就会本转换成out.write("xxxxxx")语句输出。

3、JSP基础语法(了解)

任何语言都有自己的语法。JSP作为Java技术的一个分支,它即支持所有的Java语法,也拥有一些自己扩充的语法。

3.1、JSP表达式

语法格式:

1
<%= 表达式 %>

注:

  • JSP 表达式会被服务器转换到"xxx_jsp"类的"_jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)"方法中,并转换成outprint()方法输出。
  • JSP 表达式的作用:将表达式的运行结果转化成String输出到客户端。
  • JSP 表达式也可使用**“EL(Express Language)表达式”**简化(更常用)。EL表达式语法格式为:
1
${表达式}

3.2、JSP脚本片段

语法格式:

1
<% 代码片段 %>

注:JSP 脚本片段被服务器转换到"xxx_jsp"类的"_jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)"方法中,Java代码片段会被原封不动的输出。

3.3、JSP声明

语法格式:

1
<%! 代码片段 %>

注:JSP 声明中的代码会被服务器转换到"xxx_jsp"类中,成为类的成员,Java代码片段会被原封不动的输出。

3.4、JSP注释

语法格式:

1
<%-- 注释内容 --%>

4、JSP指令(了解)

JSP指令用来设置与整个JSP页面相关的属性。

语法格式:

1
<%@ 指令 属性1="值1" 属性2="值2" …… %>
  • JSP中定义了三种指令标签:
指令 描述 典型应用
<%@ page … %> 定义页面的依赖属性 导入依赖包、自定义error页面等
<%@ include … %> 包含其他文件 将多个页面合并为一个页面等
<%@ taglib … %> 引入标签库的定义,可以是自定义标签 不常用

5、JSP动作(了解)

JSP动作元素为请求处理阶段提供信息。利用JSP动作可以动态地插入文件、重用JavaBean组件、将请求重定向到另一个页面、为Java插件生成HTML代码等。

  • 动作元素只有一种语法,它符合XML标准:
1
<jsp:动作名称 属性="值" />
  • 动作元素基本上都是预定义的函数,JSP规范定义了一系列的标准动作,它用jsp作为前缀,常用的标准动作元素如下:
语法 描述
jsp:includ 在页面被请求的时候引入一个文件。
jsp:useBean 寻找或者实例化一个JavaBean。
jsp:setProperty 设置JavaBean的属性。
jsp:getProperty 输出某个JavaBean的属性。
jsp:forward 把请求转到一个新的页面。
jsp:param 以“键值对”的形式为其它标签提供附加信息
…… ……

6、9大内置对象及作用域(重点)

  • 通过学习JSP运行原理,我们已经知道:JSP中的9大内置对象中,除exception之外,其余8个内置对象都是在服务器转换到的"xxx_jsp"类的"_jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)"方法中定义的。
  • 9大内置对象可以在JSP页面中直接使用。
序号 9大内置对象 描述 作用域 应用场景
(1) request(请求) HttpServletRequest类的实例 保存的数据只在同一次请求中有效(请求转发也会携带该数据) 常用于存放客户端向服务器发送一次请求产生的数据,即用户看完就可以清除的数据。如:新闻等。
(2) response(响应) HttpServletResponse类的实例 / /
(3) pageContext(页面上下文) PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问 保存的数据只在同一个页面中有效 常用于存放只在当前页面使用的数据。
(4) session(会话) HttpSession类的实例 保存的数据只在同一次会话中有效 常用于存放用户用完一会还有用的数据。如:购物车数据等。
(5) application(应用上下文) ServletContext类的实例,与应用上下文有关 保存的数据在**同一个Web应用(项目)**中有效,即服务器开启过程中一直有效 常用于存放一个用户用完了,其它用户还可能使用的数据。如:聊天数据等。
(6) config(配置) ServletConfig类的实例 / /
(7) out(输出对象) PrintWriter类的实例,用于把结果输出至网页上 / /
(8) page(当前页面) 类似于Java类中的this关键字 / /
(9) exception(异常) exception类的对象,代表发生错误的 JSP 页面中对应的异常对象 / /

注:application的作用域 > session的作用域 > request的作用域 > pageContext的作用域。

7、EL表达式

  • EL(Express Language)表达式的语法格式为:
1
${表达式}
  • EL表达式的作用:
    • 获取数据
    • 执行运行
    • 获取Web开发的常用对象

8、JSP标准标签库(JSTL)(了解)

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。JSTL中定义了许多标签,可以供我们使用,这些标签实现的功能和Java代码是一样的。

8.1、JSTL的使用步骤

使用JSTL总共分两步:

(1)使用JSTL中的任何库,都必须在每个 JSP 文件中的头部引用相应的标签库。

(2)使用标签库中的方法。

8.2、JSTL的分类

根据JSTL标签所提供的功能,可以将其分为5类:

(1)核心标签

核心标签是最常用的 JSTL标签。引用核心标签库的语法如下:

1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

(2)格式化标签

JSTL格式化标签用来格式化并输出文本、日期、时间、数字。引用格式化标签库的语法如下:

1
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

(3)SQL 标签

JSTL SQL标签库提供了与关系型数据库(OracleMySQLSQL Server等)进行交互的标签。引用SQL标签库的语法如下:

1
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

(4)XML 标签

JSTL XML标签库提供了创建和操作XML文档的标签。引用XML标签库的语法如下:

1
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>

(5)JSTL 函数

JSTL包含一系列标准函数,大部分是通用的字符串处理函数。引用JSTL函数库的语法如下:

1
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

(本讲完,系列博文持续更新中…… )

阿汤笔迹微信公众平台

关注**“阿汤笔迹”** 微信公众号,获取更多学习笔记。
原文地址:http://www.atangbiji.com/2022/10/11/jspInDetail
博主最新文章在个人博客 http://www.atangbiji.com/ 发布。