27. JSP

2021-11-19 16:12:59 浏览数 (1)

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.writeout.print 的区别

  • out.write() 输出字符串没有问题
  • out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)
2.1 使用 out.writeout.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.writeout.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)   "&nbsp&nbsp"%></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>
3.开启以及停止tomcat服务,查看监听器的执行

0 人点赞