27. JSP
1.什么是 jsp,它有什么用?
jsp 的全换是 java server pages。Java 的服务器页面。
jsp 的主要作用是代替 Servlet 程序回传 html 页面的数据。
因为 Servlet 程序回传 html 页面数据是一件非常繁锁的事情。开发成本和维护成本都极高。
Servlet 回传 html 页面数据的代码:
上面说到 Servlet 程序回传 html 页面数据是一件非常繁琐的事情,那么如何繁琐呢?下面我们来演示看看。
1.首先创建一个 PrintHtml 的 Servlet
Servlet 初始化程序:
image-20201124231135015
代码语言:javascript复制public class PrintHtml extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("PrintHtml GET方法");
}
}
配置 web.xml
image-20201124230923153
2.使用 getWriter()
返回字符串 html 数据。
代码语言:javascript复制package com.test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author Aron.li
* @date 2020/11/24 23:08
*/
public class PrintHtml extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("PrintHtml GET方法");
// 设置响应头
response.setContentType("text/html; charset=UTF-8");
// 使用 `getWriter()` 返回字符串 数据。
PrintWriter writer = response.getWriter();
writer.write("<!DOCTYPE html>rn");
writer.write(" <html lang="en">rn");
writer.write(" <head>rn");
writer.write(" <meta charset="UTF-8">rn");
writer.write(" <title>Title</title>rn");
writer.write(" </head>rn");
writer.write(" <body>rn");
writer.write(" <h1>这是 html 页面数据</h1> rn");
writer.write(" </body>rn");
writer.write("</html>rn");
writer.write("rn");
}
}
3.使用浏览器访问 Servlet
访问 http://localhost:8080/08_jsp/printHtml
image-20201124234607614
可以看到成功返回了 html 页面。
但是我们可以看到返回一个 html 页面需要使用大量的 writer.write("html内容")
,这就让开发的工作非常繁琐了。
那么怎么解决这个问题呢?
我们可以采用 jsp 页面来返回 html 代码。
jsp 回传一个简单 html 页面的代码:
1.在 web 目录下,创建一个 a.jsp 文件
image-20201124234908529
2.在 a.jsp 文件中,写一个简单的内容
image-20201125011519382
3.在浏览器访问 a.jsp
访问 http://localhost:8080/08_jsp/a.jsp
image-20201125011554112
可以看到写 JSP 基本跟写 HTML 一样,不用像 Servlet 回传 html 数据那么麻烦。
jsp 的小结:
1、如何创建 jsp 的页面?
输入文件名敲回车即可!!
image-20201122164423364
2、jsp 如何访问:
jsp 页面和 html 页面一样,都是存放在 web 目录下。访问也跟访问 html 页面一样。
代码语言:javascript复制比如:
在 web 目录下有如下的文件:
web 目录
a.html 页面 访问地址是 =======>>>>>> http://ip:port/工程路径/a.html
b.jsp 页面 访问地址是 =======>>>>>> http://ip:port/工程路径/b.jsp
2.jsp 的本质是什么。
jsp 页面本质上是一个 Servlet 程序。
下面我们来演示一下:
1.首先开启tomcat服务,查看当前项目的部署路径:
image-20201125083823712
我们复制该路径,进入文件夹如下:
image-20201125083925063
image-20201125084011248
2.删除部署目录下生成的文件夹
为了下面演示 jsp 部署后生成的文件,我们首先将模块部署的已生成文件删除一下。
image-20201125084655276
3.重新启动 tomcat 服务
image-20201125231234869
image-20201125231340432
4.访问 a.jsp ,查看生成的 java 代码
image-20201125231537387
也就是说,当我们第一次访问 jsp 页面的时候。Tomcat 服务器会帮我们把 jsp 页面翻译成为一个 java 源文件。并且对它进行编译成为.class 字节码程序。
5.打开生成的 a_jsp.java 源文件
image-20201125231708617
我们跟踪原代码发现,HttpJspBase 类。它直接地继承了 HttpServlet 类。也就是说。jsp 翻译出来的 java 类,它间接了继承了 HttpServlet 类。也就是说,翻译出来的是一个 Servlet 程序。
6.继续往下看源代码,可以看到底层实现也是通过输出流,将 html 页面数据回传到 客户端
image-20201125231916409
7. 总结:通过翻译的 java 源代码我们就可以得到结果:jsp 就是 Servlet 程序。
3.jsp 的三种语法
我们首先来看看 jsp 的基础代码,如下:
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>这是JSP的页面</h1>
</body>
</html>
那么整个 jsp 除了第一行之外,下面的内容基本就是跟 html 一样。所以下面我们首先来看看第一行的代码有什么作用。
a) jsp 头部的 page 指令
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
jsp 的 page 指令可以修改 jsp 页面中一些重要的属性,或者行为。
采用的参数如下:
代码语言:javascript复制1. language 属性 表示 jsp 翻译后是什么语言文件。暂时只支持 java。
2. contentType 属性 表示 jsp 返回的数据类型是什么。也是源码中 response.setContentType()参数值
3. pageEncoding 属性 表示当前 jsp 页面文件本身的字符集。
4. import 属性 跟 java 源代码中一样。用于导包,导类。
========================两个属性是给 out 输出流使用=============================
5. autoFlush 属性 设置当 out 输出流缓冲区满了之后,是否自动刷新冲级区。默认值是 true。
6. buffer 属性 设置 out 缓冲区的大小。默认是 8kb
========================两个属性是给 out 输出流使用=============================
7. errorPage 属性 设置当 jsp 页面运行时出错,自动跳转去的错误页面路径。
<!-- errorPage 表示错误后自动跳转去的路径 <br/>
这个路径一般都是以斜杠打头,它表示请求地址为 http://ip:port/工程路径/映射到代码的 Web 目录 -->
8. isErrorPage 属性 设置当前 jsp 页面是否是错误信息页面。默认是 false。如果是 true 可以 获取异常信息。
9. session 属性 设置访问当前 jsp 页面,是否会创建 HttpSession 对象。默认是 true。
10. extends 属性 设置 jsp 翻译出来的 java 类默认继承谁。
下面我们来逐个演示一下:
1. language 属性 表示 jsp 翻译后是什么语言文件。暂时只支持 java。
我们尝试将 language 设置为 c ,看看是什么情况,如下:
image-20201126073501648
在浏览器访问 a.jsp 如下:
image-20201126073659464
2. contentType 属性 表示 jsp 返回的数据类型是什么。也是源码中 response.setContentType()参数值
image-20201126074304642
在浏览器访问 a.jsp 查看响应信息如下:
image-20201126074435473
在 tomcat 部署目录中查看源码,如下:
image-20201126074801085
3. pageEncoding 属性 表示当前 jsp 页面文件本身的字符集。
image-20201126075108991
4. import 属性 跟 java 源代码中一样。用于导包,导类。
image-20201126075320547
5. autoFlush 属性 设置当 out 输出流缓冲区满了之后,是否自动刷新冲级区。默认值是 true。
6. buffer 属性 设置 out 缓冲区的大小。默认是 8kb
这两个属性是给 out 输出流使用的,下面我们来设置一下,如下:
image-20201126075755768
下面我们来设置不能自动刷新缓冲区,并且将缓冲区的大小设置为 1kb:
image-20201126075845006
再在页面设置比较多内容,尝试将输出缓冲区溢出,如下:
image-20201126075958578
再次访问浏览器如下:
image-20201126080050383
可以从上图来看,出现了 JSP缓冲区溢出 的问题,所以一般这两个参数采用默认值即可。
7. errorPage 属性 设置当 jsp 页面运行时出错,自动跳转去的错误页面路径
7.1 编写产生错误的页面
首先我们在 a.jsp 页面中写一个错误,如下:
image-20201126224416313
那么此时在浏览器访问 jsp 页面,则会显示如下:
image-20201126224510704
那么为了更加友好得展示错误页面,我们应该自定义一些错误页面。
7.2 编写自定义的错误页面
image-20201126224837032
7.3 回到 a.jsp 设置错误页面
image-20201126225010474
再次浏览器访问存在错误的 a.jsp 如下:
image-20201126225033324
8. isErrorPage 属性 设置当前 jsp 页面是否是错误信息页面。默认是 false。如果是 true 可以 获取异常信息。
这个参数不好示例,只能在源码展示一下。首先我们在 page 参数中设置如下:
image-20201126225553101
然后可以在源码中看到 异常信息 如下:
image-20201126225621235
9. session 属性 设置访问当前 jsp 页面,是否会创建 HttpSession 对象。默认是 true。
在默认情况下,jsp 源码中是创建了 HttpSession 对象的,如下:
image-20201126230107864
如果设置 session 属性为 false 如下:
image-20201126230135268
当设置了之后,再次查看 jsp 的源码,发现没有 HttpSession 的对象了,如下:
image-20201126230254496
10. extends 属性 设置 jsp 翻译出来的 java 类默认继承谁。
这个参数我们一般不会去修改,因为默认是继承 HttpJspBase 的,如下:
image-20201126230550728
通过 extends 属性,我们可以修改 jsp 源码继承的类,如下:
image-20201126230633096
再次查看源码如下:
image-20201126232120214
浏览器访问 a.jsp 如下:
image-20201126232223317
b)jsp 中的常用脚本
1. 声明脚本(极少使用)
代码语言:javascript复制声明脚本的格式是: <%! 声明 java 代码 %>
作用:可以给 jsp 翻译出来的 java 类定义属性和方法甚至是静态代码块。内部类等。
练习:
1、声明类属性
2、声明 static 静态代码块
3、声明类方法
4、声明内部类
1.1 声明类属性
下面我们在 a.jsp 中进行 声明类属性,如下:
image-20201127000834486
代码语言:javascript复制<%--1、声明类属性--%>
<%!
private Integer id;
private String name;
private static Map<String,Object> map;
%>
访问 a.jsp 后,我们来看看 jsp 的源码如下:
image-20201127001133237
1.2 声明 static 静态代码块
image-20201127001306567
代码语言:javascript复制<%--2、声明 static 静态代码块--%>
<%!
static {
map = new HashMap<String,Object>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
}
%>
jsp的源码文件生成如下:
image-20201127001347114
1.3 声明类方法
image-20201127001508326
jsp的源码文件生成如下:
image-20201127001524697
1.4 声明内部类
image-20201127001559428
代码语言:javascript复制<%--4、声明内部类--%>
<%!
public static class A {
private Integer id = 12;
private String abc = "abc";
}
%>
jsp的源码文件生成如下:
image-20201127001630025
2. 表达式脚本(常用)
代码语言:javascript复制表达式脚本的格式是: <%= 表达式 %>
表达式脚本的作用是:的 jsp 页面上输出数据。
表达式脚本的特点:
1、所有的表达式脚本都会被翻译到_jspService() 方法中
2、表达式脚本都会被翻译成为 out.print()输出到页面上
3、由于表达式脚本翻译的内容都在_jspService() 方法中,所以_jspService()方法中的对象都可以直接使用。
4、表达式脚本中的表达式不能以分号结束。
练习:
1. 输出整型
2. 输出浮点型
3. 输出字符串
4. 输出对象
2.1 输出整型
image-20201127002027945
代码语言:javascript复制<%-- 2.1 输出整型 --%>
<%=12 %> <br>
浏览器访问 a.jsp 显示如下:
image-20201127002106133
a.jsp 的源码转译如下:
image-20201127002146922
2.2 输出浮点型
image-20201127002231525
代码语言:javascript复制<%-- 2.2 输出浮点型--%>
<%=12.12 %> <br>
浏览器访问 a.jsp 显示如下:
image-20201127002256851
a.jsp 的源码转译如下:
image-20201127002325680
2.3 输出字符串
image-20201127002404183
代码语言:javascript复制<%-- 2.3 输出字符串--%>
<%="我是字符串" %> <br>
浏览器访问 a.jsp 显示如下:
image-20201127002521928
a.jsp 的源码转译如下:
image-20201127002543024
2.4 输出对象
image-20201127002634132
代码语言:javascript复制<%-- 2.4 输出对象--%>
<%=map%> <br>
<%=request.getParameter("username")%>
浏览器访问 a.jsp 显示如下:
访问http://localhost:8080/08_jsp/a.jsp?username=libai
image-20201127002753266
a.jsp 的源码转译如下:
image-20201127002825520
3. 代码脚本
代码语言:javascript复制代码脚本的格式是:
<%
java 语句
%>
代码脚本的作用是:可以在 jsp 页面中,编写我们自己需要的功能(写的是 java 语句)。
代码脚本的特点是:
1、代码脚本翻译之后都在_jspService 方法中
2、代码脚本由于翻译到_jspService()方法中,所以在_jspService()方法中的现有对象都可以直接使用。
3、还可以由多个代码脚本块组合完成一个完整的 java 语句。
4、代码脚本还可以和表达式脚本一起组合使用,在 jsp 页面上输出数据
练习:
1. 代码脚本----if 语句
2. 代码脚本----for 循环语句
3. 翻译后 java 文件中_jspService 方法内的代码都可以写
3.1 代码脚本----if 语句
image-20201127003855386
代码语言:javascript复制<%--练习:--%>
<%--1.代码脚本----if 语句--%>
<%
int i = 13;
if (i == 12){
System.out.println("this is 12!");
}else {
System.out.println("else.....");
}
%>
浏览器访问 a.jsp 显示如下:
image-20201127003939380
a.jsp 的源码转译如下:
image-20201127004132681
3.2 代码脚本----for 循环语句
image-20201127004230402
代码语言:javascript复制<%-- 3.2 代码脚本----for 循环语句--%>
<%
for (int j = 0; j < 5; j ) {
System.out.println(j);
}
%>
浏览器访问 a.jsp 显示如下:
image-20201127004407678
a.jsp 的源码转译如下:
image-20201127004426671
3.3 翻译后 java 文件中_jspService 方法内的代码都可以写
image-20201127004559445
代码语言:javascript复制<%-- 3.3 翻译后 java 文件中_jspService 方法内的代码都可以写--%>
<%
String username = request.getParameter("username");
System.out.println("用户名的请求参数值是:" username);
%>
浏览器访问 a.jsp 显示如下:
image-20201127004646059
a.jsp 的源码转译如下:
image-20201127004721663
3.4、还可以由多个代码脚本块组合完成一个完整的 java 语句。
我们用两个代码脚本块,将 for 循环拆分一下,如下:
image-20201127005015777
代码语言:javascript复制<%-- 3.4、还可以由多个代码脚本块组合完成一个完整的 java 语句。--%>
<%
for (int j = 0; j < 5; j ) {
%>
<h1>拆分代码块</h1>
<%
System.out.println(j);
}
%>
浏览器访问 a.jsp 显示如下:
image-20201127005156268
a.jsp 的源码转译如下:
image-20201127005215717
3.5 使用多个代码脚本块,渲染一个 table 表格
image-20201127005757003
代码语言:javascript复制<%-- 3.5 使用多个代码脚本块,渲染一个 table 表格--%>
<table>
<thead>
<tr>
<th>序号</th>
<th>用户名</th>
</tr>
</thead>
<tbody>
<%
for (int j = 0; j < 10; j ) {
%>
<tr>
<td>
<%=j 1%>
</td>
<td>
<%="user" j%>
</td>
</tr>
<%
}
%>
</tbody>
</table>
浏览器访问 a.jsp 显示如下:
image-20201127005828532
a.jsp 的源码转译如下:
image-20201127005911420
c)jsp 中的三种注释
1. html 注释
image-20201127081452717
代码语言:javascript复制<!-- 这是 html 注释 -->
html 注释会被翻译到 java 源代码中。在_jspService 方法里,以 out.writer 输出到客户端。
image-20201127081552381
2. java 注释
image-20201127081631326
代码语言:javascript复制<%
// 单行 java 注释
/* 多行 java 注释 */
%>
java 注释会被翻译到 java 源代码中。
image-20201127081859468
3. jsp 注释
image-20201127081927954
代码语言:javascript复制<%-- 这是 jsp 注释 --%>
jsp 注释可以注掉,jsp 页面中所有代码。
4.jsp 九大内置对象
jsp 中的内置对象,是指 Tomcat 在翻译 jsp 页面成为 Servlet 源代码后,内部提供的九大对象,叫内置对象。
image-20201127082955116
代码语言:javascript复制 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;
jsp 的九大内存对象:
代码语言:javascript复制- request 请求对象 final javax.servlet.http.HttpServletRequest request
- response 响应对象 final javax.servlet.http.HttpServletResponse response
- pageContext jsp的上下文对象 final javax.servlet.jsp.PageContext pageContext
- session 会话对象 javax.servlet.http.HttpSession session
- application ServletContext对象 final javax.servlet.ServletContext application
- config ServletConfig对象 final javax.servlet.ServletConfig config
- out jsp输出流对象 javax.servlet.jsp.JspWriter out
- page 指向当前jsp对象 final java.lang.Object page
- exception 异常对象 java.lang.Throwable exception
5.jsp 四大域对象
四个域对象分别是:
代码语言:javascript复制- pageContext (PageContextImpl 类) 当前 jsp 页面范围内有效
- request (HttpServletRequest 类)、 一次请求内有效
- session (HttpSession 类)、 一个会话范围内有效(打开浏览器访问服务器,直到关闭浏览器)
- application (ServletContext 类) 整个 web 工程范围内都有效(只要 web 工程不停止,数据都在)
域对象是可以像 Map 一样存取数据的对象。四个域对象功能一样。不同的是它们对数据的存取范围。
虽然四个域对象都可以存取数据。在使用上它们是有优先顺序的。
四个域在使用的时候,优先顺序分别是,他们从小到大的范围的顺序。
代码语言:javascript复制pageContext ====>>> request ====>>> session ====>>> application
下面我们来使用代码演示一下四个域对象的数据范围。
1.创建 scope.jsp 页面,执行四个域对象的数据存储 以及 在当前 jsp 页面读取 4 个 域对象的 值
image-20201128104637013
代码语言:javascript复制<%
// 往四个域中都分别保存了数据
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
<%-- 从四个域中读取值 --%>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
浏览器访问 http://localhost:8080/08_jsp/scope.jsp 如下:
image-20201128104744958
从上图来看,4个域的值 在当前 jsp 都是可以读取的。
2. 验证 pageContext 域对象:存储值只在当前 JSP 有效
创建 scope2.jsp 页面,提供给 scope.jsp 进行页面跳转。用于演示在不同 jsp 下,4 个域对象的 取值范围
2.1 创建 scope2.jsp 页面如下:
image-20201128105616135
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>scope2</title>
</head>
<body>
<%-- 跨页面:从四个域中读取值 --%>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
</body>
</html>
2.2 回到 scope.jsp 设置请求跳转至 scope2.jsp
image-20201128105844487
代码语言:javascript复制<html>
<head>
<title>scope</title>
</head>
<body>
<%
// 往四个域中都分别保存了数据
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
<%-- 从四个域中读取值 --%>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
<%-- 设置请求转发:跳至 scope2.jsp --%>
<%
request.getRequestDispatcher("/scope2.jsp").forward(request,response);
%>
</body>
</html>
2.3 访问 scope.jsp ,页面会自动跳至 scope2.jsp,查看 4 个域对象 的 取值
访问 http://localhost:8080/08_jsp/scope.jsp 如下:
image-20201128110049715
可以从效果来看, pageContext 只会在 存储值的当前 JSP 页面有效,当跳转至其他页面,则无法取值。
3.验证 request 域对象: 存储值 只在一次请求中有效
执行第二次请求 scope2.jsp,也就是说进行了多次 request 请求,那么验证 request 域对象无法获取 上一次 的存储值
image-20201128110443269
说明:request 域对象 的存储值 只在一次请求中有效。
4.验证 session 域对象: 一个会话范围内有效(打开浏览器访问服务器,直到关闭浏览器)
关闭我们之前打开的浏览器(结束会话),然后再次打开 访问 scope2.jsp ,如下:
http://localhost:8080/08_jsp/scope2.jsp
image-20201128110937277
5.验证 application 对象: 整个 web 工程范围内都有效(只要 web 工程不停止,数据都在)
image-20201128111118328
scope.jsp 页面
代码语言:javascript复制<body>
<h1>scope.jsp 页面</h1>
<%
// 往四个域中都分别保存了数据
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
<%
request.getRequestDispatcher("/scope2.jsp").forward(request,response);
%>
</body>
scope2.jsp 页面
代码语言:javascript复制<body>
<h1>scope2.jsp 页面</h1>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
</body>
6.jsp 中的 out 输出和 response.getWriter 输出的区别
- response 中表示响应,我们经常用于设置返回给客户端的内容(输出)
- out 也是给用户做输出使用的。
当时这两者如果同时使用的话,可能返回给浏览器的顺序会存在一定的问题。
1.演示 out.write()
和 response.getWriter.write()
输出数据到浏览器的顺序
1.1 创建 output.jsp 如下:
image-20201128200343375
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 使用out输出页面数据
out.write("out write 1 <br>");
out.write("out write 2 <br>");
// 使用response输出页面数据
response.getWriter().write("response write 1 <br>");
response.getWriter().write("response write 2 <br>");
%>
</body>
</html>
1.2 使用浏览器访问 output.jsp ,查看 html 上显示的数据顺序
访问 http://localhost:8080/08_jsp/output.jsp
image-20201128201059191
可以在浏览器看到 显示的 html 内容顺序没有按照 jsp 源码中的设置,就算 out.write()
在代码的上方,依然优先显示了 response
的输出内容。
这是为什么呢?
1.3 分析 out 输出内容 在 response 内容之后的问题
当 JSP 页面中所有代码执行完成后,会进行以下两个操作:
- 执行
out.flush()
操作,将 out 缓冲区中的数据 追加写入 response 缓冲器的末尾 - 执行
response
的刷新操作,把 缓冲区内的 全部数据 传递到客户端
执行示意图如下:
image-20201128212433579
在这个执行过程中,最重要的就是执行 out.flush()
时机,我们可以主动调用,来设置 out.write
写入到 response
缓冲区的位置。
1.4 主动调用 out.flush()
设置 out.write
写入 response
缓冲区的位置
image-20201128212824449
代码语言:javascript复制<%
// 使用out输出页面数据
out.write("out write 1 <br>");
out.write("out write 2 <br>");
// 主动调用 out.flush, 将上面 out.write 写入到 response 缓冲区中
out.flush();
// 使用response输出页面数据
response.getWriter().write("response write 1 <br>");
response.getWriter().write("response write 2 <br>");
%>
执行示意图如下:
image-20201128213220190
所以,我们只要通过调用 out.flush
来改变 out.write
写入 response
缓冲区的时机,就可以控制输出内容的顺序。
2.out.write
与 out.print
的区别
- out.write() 输出字符串没有问题
- out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)
2.1 使用 out.write
、out.print
输出字符串
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 使用out.write输出字符串
out.write("12");
// 使用out.print输出字符串
out.print("12");
%>
</body>
</html>
使用浏览器访问如下:
image-20201128215151028
2.2 使用 out.write
、out.print
输出 整数
代码语言:javascript复制<%
// 使用out.write输出整数
out.write(12);
// 使用out.print输出整数
out.print(12);
%>
浏览器访问如下:
image-20201128215353636
2.3 总结
out.write() 输出字符串没有问题
out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)
深入源码,浅出结论:在 jsp 页面中,可以统一使用 out.print()来进行输出
7.jsp 的常用标签
一般来说,一个页面的架构基本是基于如下的架构:
image-20201129081800699
如果这个架构可能要写很多个页面,那么其中 Footer
部分 一般都是写 版权的内容,如果有 1000 个页面,此时如果要修改 版权内容,那就要修改 1000 个页面下面的 Footer 部分。
一般这种情况,我们可以将 Footer 部分抽离出来,统一管理。
而管理的处理方式,我们可以使用 jsp 的 静态包含。
a)jsp 静态包含
示例说明:
代码语言:javascript复制 <%--
<%@ include file=""%>
就是静态包含 file 属性指定你要包含的 jsp 页面的路径
地址中第一个斜杠 / 表示为 http://ip:port/工程路径/ 映射到代码的 web 目录
静态包含的特点:
1、静态包含不会翻译被包含的 jsp 页面。
2、静态包含其实是把被包含的 jsp 页面的代码拷贝到包含的位置执行输出。
--%>
<%@ include file="/include/footer.jsp"%>
1.在 web 工程目录下创建 include 目录,再创建 footer.jsp 以及 main.jsp,其中 footer.jsp 用来写底部内容如下:
image-20201129083014866
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
脚页信息 <br>
</body>
</html>
2.整体结构的 main.jsp 使用 jsp静态包含,引入 footer.jsp 的内容,如下:
image-20201129083103956
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
头部信息 <br>
主体内容 <br>
<%--
<%@ include file=""%>
就是静态包含 file 属性指定你要包含的 jsp 页面的路径
地址中第一个斜杠 / 表示为 http://ip:port/工程路径/ 映射到代码的 web 目录
静态包含的特点:
1、静态包含不会翻译被包含的 jsp 页面。
2、静态包含其实是把被包含的 jsp 页面的代码拷贝到包含的位置执行输出。
--%>
<%@ include file="/include/footer.jsp"%>
</body>
</html>
3.使用浏览器访问效果如下:
image-20201129083310444
4.打开 main.jsp 源码,查看静态代码的作用:就是将 footer.jsp 源码拷贝过来
image-20201129083738026
image-20201129083543632
b)jsp 动态包含
示例说明:
代码语言:javascript复制<%--
<jsp:include page=""></jsp:include>
这是动态包含
page 属性是指定你要包含的 jsp 页面的路径
动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置
动态包含的特点:
1、动态包含会把包含的 jsp 页面也翻译成为 java 代码
2、动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false);
3、动态包含,还可以传递参数
--%>
<jsp:include page="/include/footer.jsp">
<jsp:param name="username" value="bbj"/>
<jsp:param name="password" value="root"/>
</jsp:include>
1.将上面示例的静态包含,改为动态包含
动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置:
image-20201129084308016
代码语言:javascript复制<%-- 动态包含
<jsp:include page=""></jsp:include>
这是动态包含
page 属性是指定你要包含的 jsp 页面的路径
动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置
--%>
<jsp:include page="/include/footer.jsp"></jsp:include>
浏览器访问效果如下:
image-20201129084347839
为了做区分,我在 footer.jsp 的内容修改了一下,如下:
image-20201129084424276
2. 动态包含的特点 1 :动态包含会把包含的 jsp 页面也翻译成为 java 代码
image-20201129084629174
3、动态包含的特点2:动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
代码语言:javascript复制JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false);
打开 main_jsp.java
如下:
image-20201129084917807
4、动态包含特点3:动态包含,还可以传递参数
4.1 首先在 main.jsp 中设置需要传递的参数
image-20201129090531937
代码语言:javascript复制<jsp:include page="/include/footer.jsp">
<jsp:param name="user" value="libai"/>
<jsp:param name="password" value="feilao"/>
</jsp:include>
4.2 在 footer.jsp 接收参数
image-20201129090645775
代码语言:javascript复制<%-- 接收动态包含传递的参数: --%>
<%=request.getParameter("password")%>
4.3 在浏览器访问效果如下:
image-20201129090739064
5. 动态包含的底层原理:
c)jsp 标签-转发
示例说明:
代码语言:javascript复制<%--
<jsp:forward page=""></jsp:forward> 是请求转发标签,它的功能就是请求转发
page 属性设置请求转发的路径
--%>
<jsp:forward page="/scope2.jsp"></jsp:forward>
1.下面我们简单地在 main.jsp 设置转发至另一个之前写的 scope2.jsp 页面中
image-20201129091725200
代码语言:javascript复制<%--
<jsp:forward page=""></jsp:forward> 是请求转发标签,它的功能就是请求转发
page 属性设置请求转发的路径
--%>
<jsp:forward page="/scope2.jsp"></jsp:forward>
2. 访问 main.jsp,浏览器显示如下:
image-20201129091903962
8、jsp 的练习题
练习一:在 jsp 页面中输出九九乘法口诀表
1.创建test目录,在里面写 test.jsp 如下:
image-20201129101609668
代码语言:javascript复制<%-- 九九乘法表 --%>
<%
// 外层循环
for (int i = 1; i <= 9; i ) {
// 内层循环
for (int j = 1; j <= i; j ) {
// 打印九九乘法表内容
System.out.print(j "*" i "=" (i*j) "t" );
}
// 换行
System.out.println();
}
%>
访问 test.jsp,查看打印的信息如下:
image-20201129101749773
2.将打印的内容设置为 table 表格,显示在页面中
image-20201129103404633
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%-- 九九乘法表 --%>
<h1 align="center">九九乘法表</h1>
<table align="center">
<%-- 外层循环 --%>
<% for (int i = 1; i <= 9; i ) { %>
<%-- 表格的行 --%>
<tr>
<%-- 内层循环 --%>
<% for (int j = 1; j <= i; j ) { %>
<%-- 表格的列 --%>
<td><%=j "*" i "=" (i*j) "  "%></td>
<% } %>
</tr>
<% } %>
</table>
</body>
</html>
浏览器显示如下:
image-20201129102906180
练习二:jsp 输出一个表格,里面有 10 个学生信息。
跟上面练习一大同小异,只是这次输出的内容是10个学生类的信息。
1.创建Student类
首先创建一个 Student 类,用来存储 学生的信息。
image-20201129104919591
代码语言:javascript复制package com.pojo;
/**
* @author Aron.li
* @date 2020/11/29 10:47
*/
public class Student {
// 成员属性
private Integer id; // 学生ID
private String name; // 学生名称
private Integer age; // 学生年龄
private String phone; // 电话号码
// 构造器
public Student() {
}
public Student(Integer id, String name, Integer age, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.phone = phone;
}
// toString
@Override
public String toString() {
return "Student{"
"id=" id
", name='" name '''
", age=" age
", phone='" phone '''
'}';
}
// getter setter
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
2.创建 test2.jsp,用于创建 Student 类对象、以及显示 Student 类对象的数据
2.1 创建10个 Student 类对象
image-20201129105436086
2.2 显示 Student 类对象的数据
image-20201129111545611
代码语言:javascript复制<%@ page import="com.pojo.Student" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %><%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/11/29
Time: 10:50
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>
<%--创建10个Student类对象--%>
<%
List<Student> studentList = new ArrayList<>();
for (int i = 1; i <= 10; i ) {
studentList.add(new Student(i, "name" i, 18 i, "电话" i));
}
System.out.println(studentList.toString());
%>
<%--显示Student类对象--%>
<h1 align="center">Student数据</h1>
<table align="center">
<thead>
<tr>
<th>学生ID</th>
<th>学生名称</th>
<th>学生年龄</th>
<th>电话</th>
</tr>
</thead>
<tbody>
<% for ( Student s: studentList) { %>
<tr>
<td><%=s.getId()%></td>
<td><%=s.getName()%></td>
<td><%=s.getAge()%></td>
<td><%=s.getPhone()%></td>
</tr>
<% } %>
</tbody>
</table>
</body>
</html>
浏览器显示如下:
image-20201129111622548
jsp 请求转发 的 使用
在上面的练习二中,我们直接在 jsp 中创建了 Student 类对象数据,然后又展示渲染出来。
下面我们使用一个 Servlet 程序来创建数据,然后在 jsp 页面中渲染出来。
一般的页面操作流程如下:
image-20201124081656990
1.创建 SearchStudentServlet 程序
image-20201129114445923
代码语言:javascript复制public class SearchStudentServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取参数
//2.连接数据库,查询数据
//以上两个步骤采用直接生成数据模拟
List<Student> studentList = new ArrayList<>();
for (int i = 1; i <= 10; i ) {
studentList.add(new Student(i, "name" i, 18 i, "电话" i));
}
//3.将查询到的数据 保存到 request 域对象
request.setAttribute("studentList", studentList);
//4.请求转发至 jsp 页面
request.getRequestDispatcher("test/showStudentInfo.jsp").forward(request, response);
}
}
2.创建显示学生信息的 jsp 页面
image-20201129114543920
代码语言:javascript复制<%@ page import="com.pojo.Student" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--获取10个Student类对象--%>
<%
List<Student > studentList = (List<Student>) request.getAttribute("studentList");
%>
<%--显示Student类对象--%>
<h1 align="center">Student数据</h1>
<table align="center">
<thead>
<tr>
<th>学生ID</th>
<th>学生名称</th>
<th>学生年龄</th>
<th>电话</th>
</tr>
</thead>
<tbody>
<% for ( Student s: studentList) { %>
<tr>
<td><%=s.getId()%></td>
<td><%=s.getName()%></td>
<td><%=s.getAge()%></td>
<td><%=s.getPhone()%></td>
</tr>
<% } %>
</tbody>
</table>
</body>
</html>
3.访问 Servlet 程序,显示如下:
image-20201129114630630
9、什么是 Listener 监听器?
1、Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监
听器。
2、Listener 它是 JavaEE 的规范,就是接口
3、监听器的作用是,监听某种事物的变化。然后通过回调函数,反馈给客户(程序)去做一些相应的处理。
9.1、ServletContextListener 监听器
ServletContextListener 它可以监听 ServletContext 对象的创建和销毁。
ServletContext 对象在 web 工程启动的时候创建,在 web 工程停止的时候销毁。
监听到创建和销毁之后都会分别调用 ServletContextListener 监听器的方法反馈。
两个方法分别是:
代码语言:javascript复制public interface ServletContextListener extends EventListener {
/**
* 在 ServletContext 对象创建之后马上调用,做初始化
*/
public void contextInitialized(ServletContextEvent sce);
/**
* 在 ServletContext 对象销毁之后调用
*/
public void contextDestroyed(ServletContextEvent sce);
}
如何使用 ServletContextListener 监听器监听 ServletContext 对象。
使用步骤如下:
1、编写一个类去实现 ServletContextListener
2、实现其两个回调方法
3、到 web.xml 中去配置监听器
1.创建监听器实现类 MyServletContextListenerImpl
image-20201129115836861
代码语言:javascript复制public class MyServletContextListenerImpl implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext 对象被创建了");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext 对象被销毁了");
}
}
2.配置 web.xml 使用 监听器
image-20201129115934273
代码语言:javascript复制<!--配置监听器-->
<listener>
<listener-class>com.listen.MyServletContextListenerImpl</listener-class>
</listener>