JSP教程
什么是JSP?
JSP(JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。
JSP将Java代码和特定变动内容嵌入到静态的页面中,实现以静态页面为模板,动态生成其中的部分内容。JSP引入了被称为“JSP动作”的XML标签,用来调用内建功能。另外,可以创建JSP标签库,然后像使用标准HTML或XML标签一样使用它们。标签库能增强功能和服务器性能,而且不受跨平台问题的限制。
JSP文件在运行时会被其编译器转换成更原始的Servlet代码。JSP编译器可以把JSP文件编译成用Java代码写的Servlet,然后再由Java编译器来编译成能快速执行的二进制机器码,也可以直接编译成二进制码。
总结:
jsp 的主要作用是代替 Servlet 程序回传 html 页面的数据。
因为 Servlet 程序响应 html 页面数据是一件非常繁锁的事情。开发成本和维护成本都极高。
Servlet响应html页面数据的代码
public class HtmlServlet extends HttpServlet {
private static final long serialVersionUID = -664392981001L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// 设置返回的数据内容的数据类型和编码
response.setContentType("text/html; charset=utf-8");
try (
ServletOutputStream os = resp.getOutputStream();
) {
// 获取字符输出流
Writer writer = response.getWriter();
//输出页面内容!
writer.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"
\"http://www.w3.org/TR/html4/loose.dtd\">");
writer.write("<html>");
writer.write("<head>");
writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html;
charset=UTF-8\">");
writer.write("<title>Insert title here</title>");
writer.write("</head>");
writer.write("<body>");
writer.write("这是由 Servlet 程序输出的 html 页面内容!");
writer.write("</body></html>");
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结:
通过分析上述代码,我们可以明显看出,使用 Servlet 输出简单的 HTML 页面信息相当不便捷。当需要生成复杂页面时,问题更为突出,不仅增加了难度,而且不利于页面的维护和调试。因此,Sun 公司为了解决这个问题,推出了 JSP(JavaServer Pages)这一动态页面技术。
JSP 旨在简化页面输出工作,让开发者能够更轻松地创建、维护和调试动态网页。
使用jsp回传html页面的代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
这是一个动态的html 页面数据
</body>
</html>
注意事项:
1、jsp 页面是一个类似于 html 的一个页面。 jsp 直接存放到 web目录下,和 html 一样访问 jsp 的时候,也和访问 html 一样。
2、jsp 的默认编码集是 iso-8859-1 修改 jsp 的默认编码为 UTF-8。
jsp 底层的运行原理
jsp 页面本质上是一个 Servlet 程序。
首先我们去找到我们 Tomcat 的目录下的 work\Catalina\localhost 目录。当我们发布 day03 项目,并启动 Tomcat服务器后,我们发现在 work\Catalina\localhost 目录下多出来一个 day03目录。
Tomcat服务器启动时,控制台输出加载Tomcat相关目录信息,找到以下目录到资源管理器定位:
Using CATALINA_BASE: "C:\Users\lison\AppData\Local\JetBrains\IntelliJIdea2023.1\tomcat\27dea85b-c644-4a4f-bbef-74c234438fc1"

一开始该目录下是没有day03这个目录的。
当我们在浏览器地址栏输入jsp页面的访问路径时,此时马上会生成一个day03\org\apache\jsp目录,并且有两个文件:

index_jsp.class 文件很明显是 index_jsp.java 源文件编译后的字节码文件。
那么 index_jsp.java 是个什么内容呢?
生成的 java 文件名,是以原来的文件名加上_jsp 得到。 xxxx_jsp.java 文件的名字。
当我们第一次访问 jsp 页面的时候。Tomcat 服务器会帮我们把 jsp 页面翻译成为一个 java 源文件。并且对它进行编译为.class 字节码文件。我们打开 java 源文件不难发现其里面的内容是:
public final class a_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
}
跟踪源代码发现HttpJspBase 类直接地继承了 HttpServlet 类。也就是说。jsp 翻译出来的 java 类,它间接了继承了 HttpServlet 类。也就是说,翻译出来的是一个 Servlet 程序。

通过分析翻译后的 Java 源代码,我们可以得出论:
JSP(JavaServer Pages)实际上就是特殊的 Servlet 程序。观察这些 Servlet 程序的源代码,我们可以发现其底层实现也是通过输出流来完成的。具体来说,HTML 页面数据被翻译并直接输出到 Servlet 程序的 service 方法中,从而实现了动态网页的生成。这就是我们所说的 JSP 是专门用于输出 HTML 页面的 Servlet 程序。
JSP底层源码
客户端发送请求,Tomcat服务器生成的java源文件中代码,该类间接继承了HttpServelet类,动态重写了service()方法。
public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
throws java.io.IOException, ServletException {
final String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
final PageContext pageContext;
HttpSession session = null;
final ServletContext application;
final ServletConfig config;
JspWriter out = null;
final 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("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" <h1>Hello,javaWeb</h1>\n");
out.write(" <div>\n");
out.write(" <form action=\"http://localhost:8080/day01-hello/hello2\" method=\"post\">\n");
out.write(" <label>\n");
out.write(" <input type=\"text\" name=\"uname\"/>\n");
out.write(" </label><br/>\n");
out.write(" <label>\n");
out.write(" <input type=\"text\" name=\"price\"/>\n");
out.write(" </label><br/>\n");
out.write(" <input type=\"submit\" value=\"提交\">\n");
out.write(" </form>\n");
out.write(" </div>\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (Throwable t) {
if (!(t instanceof 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);
}
}
结论:
从生成的文件我们不难发现一个规则。
a.jsp 翻译成 java 文件后的全名是 a_jsp.java 文件
b.jsp 翻译成 java 文件后的全名是 b_jsp.java 文件
那么 当我们访问 一个 xxx.jsp 文件后 翻译成 java 文件的全名是 xxx_jsp.java 文件, xxx_jsp.java 文件是一个 Servlet 程序。原来 jsp 中的 html 内容都被翻译到 Servlet 类的 service 方法中原样输出。
JSP语法
page指令
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
这是 jsp 文件的头声明。表示这是 jsp 页面。
page指令中的属性
language 属性值只能是 java。 表示翻译的得到的是 java语言的
contentType 属性 设置响应头 contentType 的内容
pageEncoding 属性 设置当前 jsp 页面的编码
import 属性 给当前 jsp 页面导入需要使用的类包
autoFlush 属性 设置是否自动刷新 out 的缓冲区,默认为 true
buffer 属性 设置 out 的缓冲区大小。默认为 8KB
errorPage 属性 设置当前 jsp 发生错误后,需要跳转到哪个页面去显示错误信息
isErrorPage 属性 设置当前 jsp 页面是否是错误页面。是的话,就可以使用 exception 异常对象
session 属性 设置当前 jsp 页面是否获取 session 对象,默认为 true
extends 属性 给服务器厂商预留的 jsp 默认翻译的 servlet 继承于什么类
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.Map"
errorPage="/500.jsp"
autoFlush="true"
buffer="8kb"
session="true"
extends="java.lang.Object"
%>
<!--
errorPage表示jsp发送错误后自动跳转到错误页面路径
这个路径一般都是以 / 开头,它表示请求地址为http://ip:port/工程路径/ 映射到工程的web目录
-->
Jsp中的三种脚本
1、声明式脚本
脚本格式:
<%!
java 代码
%>
在声明脚本块中,我们可以干 4 件事情:
- 我们可以定义全局变量。
- 定义 static 静态代码块
- 定义方法
- 定义内部类
几乎可以写在类的内部写的代码,都可以通过声明脚本来实现。
<%!
//全局变量
public static final long NUMBER = 63712119L;
public static Map<String,Object> map;
public int num = 100;
//静态代码块
static {
}
//内部类
class AA {
}
//方法
public void show() {
}
2、表达式脚本
表达式脚本格式如下: <%=表达式 %>
表达式脚本 用于向jsp页面输出内容。
表达式脚本 翻译到 Servlet 程序的 service 方法中 以 out.print() 打印输出
out 是 jsp 的一个内置对象,用于生成 html 的源代码
注意:表达式不要以分号结尾,否则会报错
表达式脚本可以输出任意类型。
比如:
1.输出整型
2.输出浮点型
3.输出字符串
4.输出对象
<%=100 %><br/>
<%=map %><br/>
<%=88.88 %><br/>
<%=false %><br/>
<%="表达式脚本" %><br/>
3、代码脚本
代码脚本格式: <% java 代码 %>
代码脚本里可以书写任意的 java 语句。
代码脚本的内容都会被翻译到 service 方法中。
所以 service 方法中可以写的 java 代码,都可以书写到代码脚本中。
<%
//遍历输出偶数
var num = 100;
for (var i = 0; i <= num; i++) {
if (i%2 == 0) {
//write写入除字符串到客户端出现乱码
//out.write(i);
out.print(i);
}
}
%>
jsp中的注释
java注释
// 单行 java 注释
/*
多行 java 代码注释
*/
单行注释和多行注释能在翻译后的 java 源代码中看见。
jsp注释
<%-- jsp 注释 --%>
jsp 注释在翻译的时候会直接被忽略掉
html注释
html 的注释会被翻译到 java 代码中输出到 html 页面中查看
jsp 九大内置对象
jsp中的内置对象,指Tomcat在翻译jsp页面成为servlet源代码后,内部提供的九大对象,叫内置对象。
打开翻译后的 java 文件。查看_jspService 方法:
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;
//========九大内置对象==============
java.lang.Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);
if (exception != null) {
response.setStatus(javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
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;
//========九大内置对象==============
}
jsp 中九大内置对象说明:
request 对象 可以获取请求信息
response 对象 可以设置响应信息
pageContext 对象 当前页面上下文对象。可以在当前上下文保存属性信息
session 对象 可以获取会话信息。
exception 对象 只有在 jsp 页面的 page 指令中设置 isErrorPage="true" 的时候才会存在
application 对象 ServletContext 对象实例,可以获取整个工程的一些信息。
config 对象 ServletConfig 对象实例,可以获取 Servlet 的配置信息
out 对象 输出流。
page 对象 表示当前 Servlet 对象实例(无用,用它不如使用 this 对象)。
九大内置对象,都是我们可以在【代码脚本】中或【表达式脚本】中直接使用的对象。
jsp 四大域对象
pageContext 当前jsp页面范围内有效
request 一次请求范围内有效
session 一次会话范围内有效 (浏览器访问服务器开始带关闭浏览器的范围)
application(ServletContext) 就是 ServletContext 对象,整个web工程范围内有效(web工程启动到结束的范围内)
四大域对象经常用来保存数据信息。
测试四大作用域:
新建两个 jsp 页面。分别取名叫:context1.jsp,context2.jsp
context1.jsp
<%--
Created by IntelliJ IDEA.
User: lisong
Date: 2023-07-25
Time: 17:14
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>context1页面:</h2>
<%
//四个域中分别保存数据
pageContext.setAttribute("key1","pageContext");
request.setAttribute("key2","request");
session.setAttribute("key3","session");
application.setAttribute("key4","application");
%>
<%-- 测试当前jsp页面作用域 --%>
<%=pageContext.getAttribute("key1")%><br/>
<%=request.getAttribute("key2")%><br/>
<%=session.getAttribute("key3")%><br/>
<%=application.getAttribute("key4")%><br/>
<%
//测试request作用域
request.getRequestDispatcher("/context2.jsp").forward(request,response);
%>
</body>
</html>
context2.jsp
<%--
Created by IntelliJ IDEA.
User: lisong
Date: 2023-07-25
Time: 17:14
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>context2页面:</h2>
<%=pageContext.getAttribute("key1")%> <br/>
<%=request.getAttribute("key2")%> <br/>
<%=session.getAttribute("key3")%> <br/>
<%=application.getAttribute("key4")%> <br/>
</body>
</html>
测试pageContext作用域:
直接访问 context1.jsp 文件
测试request作用域:
1.在 context1.jsp 文件中添加转发到 context2.jsp(有数据)
2.直接访问 context2.jsp 文件 (没有数据)
测试 session 作用域:
1.访问完 context1.jsp 文件
2.关闭浏览器。但是要保持服务器一直开着
3.打开浏览器,直接访问 context2.jsp 文件
测试 application 作用域步骤:
1.访问完 context1.jsp 文件,然后关闭浏览器
2.停止服务器。再启动服务器。
3.打开浏览器访问 context2.jsp 文件
jsp 中 out 输出流 和 response.getwriter() 输出流
jsp 中 out 和 response 的 writer 的区别演示
<%--
Created by IntelliJ IDEA.
User: lisong
Date: 2024-01-15
Time: 00:27
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// out 输出
out.write("这是 out 的第一次输出<br/>");
//flush后,输出的内容会被立即响应给客户端
out.flush();
// 最后一次的输出,由于没有手动 flush,会在整个页面输出到客户端的时候,自动写入到 writer缓冲区
out.write("这是 out 的第二次输出<br/>");
/*
response.getWriter().write输出内容优先级高于out.write
out.flush后语句优先级高于response.getWriter().write
response.getWriter().close()后面的语句将不会被执行
*/
response.getWriter().write("这是 writer 的第一次输出<br/>");
response.getWriter().close();
response.getWriter().write("这是 writer 的第二次输出<br/>");
%>
</body>
</html>
最终测试结果:

out 流和 writer 流的两个缓冲区工作说明:
response对象用于设置返回给客户端的内容(输出),而out对象也用于向用户提供输出。
- 在JSP页面中,输出流按照从上到下的顺序执行。在没有执行flush语句和close()语句的情况下,response输出流的优先级高于out输出流,因此会先执行response缓冲区的内容,然后再执行out缓冲区的内容。
- 如果在out输出流之后执行flush操作,那么out缓冲区的内容将立即响应给客户端。
- 如果对response输出流执行close操作,那么后续的JSP语句将不会被执行。这是因为关闭response输出流会终止当前的HTTP响应,导致后续的输出无法发送给客户端。
由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下,我们在 jsp 页面中统一使用 out 来进行输出。避免打乱页面输出内容的顺序。
out.write() 输出字符串没有问题,输出其它数据类型会乱码 。
out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)。
<%
out.print(100);
out.print("哈哈");
%>
结论:在 jsp 页面中,可以统一使用 out.print()来进行输出。
jsp 的常用标签
jsp 静态包含
<%@ include file="/include/footer.jsp"%>
file 属性指定你要包含的 jsp 页面的路径
地址中第一个斜杠 / 表示为 http://ip:port/工程路径/ 映射到web项目的 web 目录。
静态包含是把包含的页面内容原封装不动的输出到包含的位置。
静态包含的特点:
1、静态包含不会翻译被包含的 jsp 页面。
2、静态包含其实是把被包含的 jsp 页面的代码拷贝到包含的位置执行输出。
jsp 动态包含
<jsp:include page="/include/footer.jsp">
<jsp:param name="username" value="bbj"/>
<jsp:param name="password" value="root"/>
</jsp:include>
page 属性是指定你要包含的 jsp 页面的路径动态包含也可以像静态包含一样。
把被包含的内容执行输出到包含位置
动态包含的特点:
1、动态包含会把包含的 jsp 页面单独翻译成 servlet 文件,然后在执行时再调用翻译的 servlet 程序。并把计算的结果返回。 动态包含是在执行的时候,才会加载。所以叫动态包含。
2、动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false);
3、动态包含,还可以传递参数
jsp 标签-转发
<jsp:forward page="/scope2.jsp"></jsp:forward>
<jsp:forward> 转发功能相当于
request.getRequestDispatcher("/xxxx.jsp").forward(request, response); 的功能。
静态包含和动态包含的区别:
- 是否生成java文件
静态包含在jsp底层java文件中不会被生成,动态包含会被生成。 - service()方法的区别
静态包含的内容会被拷贝到底层service()中,动态包含会调用JspRuntiemLibrary.include
方法。 - 是否可以传递参数
静态包含不可以传递参数,动态包含可以。 - 编译次数
静态包含1次,动态包含的文件+1 - 适用范围
静态包含使用纯静态(CSS,HTML,JS)内容,或没有非常耗时操作,或大量的java代码的jsp。
动态包含需要传递参数,含有大量java变量。
开发经验:
实际工作中,几乎都是使用静态包含。理由很简单。因为 jsp 页面虽然可以写 java 代码,做其他的功能操作。但是由于 jsp 在开发过程中被定位为专门用来展示页面的技术。也就是说。jsp 页面中,基
本上只有 html,css,js。还有一些简单的 EL,表达式脚本等输出语句。所以我们都使用静态包含。
JSP综合练习
在 jsp 页面中输出九九乘法口诀表
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style type="text/css">
table{
width: 650px;
}
</style>
</head>
<body>
<%-- 练习一:在 jsp 页面中输出九九乘法口诀表 --%>
<h1 align="center">九九乘法口诀表</h1>
<table align="center">
<%-- 外层循环遍历行 --%>
<% for (int i = 1; i <= 9; i++) { %>
<tr>
<%-- 内层循环遍历单元格 --%>
<% for (int j = 1; j <= i ; j++) { %>
<td><%=j + "x" + i + "=" + (i*j)%></td>
<% } %>
</tr>
<% } %>
</table>
</body>
</html>