JavaWeb 分享2

464 阅读22分钟

实际操作

用户的登录和注册

编写 Web层

  • web页面添加基础路径
  • 注册页面 regist.html
<!-- 添加 base标签,永远固定相对路径跳转的基础路径 -->
<base href="http://localhost:8080/book_project/">
  • 添加对应的注册功能 RegistServlet
  • 请求转发 req.getRequestDispatcher("/path")
package com.atjava.web;

import com.atjava.pojo.User;
import com.atjava.service.UserService;
import com.atjava.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author lv
 * @create 2021-08-07 22:21
 */
public class RegistServlet extends HttpServlet {

    private UserService us = new UserServiceImpl();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doPost(req, resp);
        // 1.获取请求参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        String code = req.getParameter("code");
        // 2.检察验证码是否正确 - 当前写死 12345
        if ("12345".equalsIgnoreCase(code)) {
            System.out.println("code is right");

            // 3.检察用户名是否可用
            boolean existsUsername = us.existsUsername(username);
            if (existsUsername) {
                System.out.println("用户名已存在");
                req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);

            } else {
                System.out.println("用户名可注册");
                us.registUser(new User(null, username, password, email));
                req.getRequestDispatcher("/pages/user/regist_success.html").forward(req, resp);

            }

        } else {
            System.out.println("code[ " + code + " ] is error");
            // 跳回注册页面
            // 请求转发,必须以 ‘/’开始,并且默认在 "web"目录下
            req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
        }


    }
}

IDEA中 Debug调试的使用

Debug调试代码,首先需要两个元素: 断点 + Debug启动服务器

  1. 断点: 只需在代码要停掉的行的左边单击,就可以添加和取消
  2. Debug启动 Tomcat运行代码

测试工具栏:

  1. 折弯箭头: 让代码往下执行一行
  2. 向下箭头: 可以进入当前方法体内 (自己写的代码,非框架源码)
  3. 向上箭头: 跳出当前方法体外,与 2相反
  4. (红色)向下箭头: 强制进入当前方法体内
  5. 斜向下箭头: 跳到光标所在处,相当于临时断点 (一次性)

变量窗口 - variables

  • 它可以查看当前方法范围内所有有效的变量

方法调用栈窗口 - Frames

  1. 它可以查看当前线程有哪些方法调用信息
  2. 下一行的方法调用上一行的方法,显示方法的调用关系

其它常用调试相关按钮 (左边竖栏)

  1. 绿色箭头: 程序一直执行,直到遇到下一个断点停止,如果没有断点,就一直执行下去
  2. 红色斜杠: 临时禁用所有断点

用户登录功能的实现

<base href="http://localhost:8080/book_project/" />
...
<form action="login" method="post">
...
</form>
package com.atjava.web;

import com.atjava.pojo.User;
import com.atjava.service.UserService;
import com.atjava.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author lv
 * @create 2021-08-08 11:08
 */
public class LoginServlet extends HttpServlet {

    private UserService us = new UserServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doPost(req, resp);
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        User user = us.login(new User(null, username, password, null));
        if (null != user) {
            System.out.println(user);
            req.getRequestDispatcher("/pages/user/login_success.html").forward(req, resp);
        } else {
            req.getRequestDispatcher("/pages/user/login.html").forward(req, resp);
        }

    }
}

JSP

jsp及它的作用

jsp的全称是 java serverpages, java的服务器页面;

jsp的主要作用是 代替 Servlet程序回传 html页面数据;

因为 Servlet程序回传 html页面数据是一件非常繁琐的事,开发成本和维护成本极高.

  • servlet方式:
package com.atjava.servlet;

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 lv
 * @create 2021-08-08 14:38
 */
public class PringHtml extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doGet(req, resp);

        // 防止乱码
        // req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=UTF-8");

        // 方式一:通过响应的输出流回传 html页面数据
        PrintWriter respWriter = resp.getWriter();
        respWriter.write("<!DOCTYPE html>\r\t");
        respWriter.write("<html lang="en">\r\t");
        respWriter.write("<head>\r\t");
        respWriter.write("    <meta charset="UTF-8">\r\t");
        respWriter.write("    <title>jsp</title>\r\t");
        respWriter.write("</head>\r\t");
        respWriter.write("<body>\r\t");
        respWriter.write("                这是 html页面数据\r\t");
        respWriter.write("</body>\r\t");
        respWriter.write("</html>\r\t");

        // 方式二:通过 jsp回传数据
//<!DOCTYPE html>
//<html lang="en">
//<head>
//    <meta charset="UTF-8">
//    <title>jsp</title>
//</head>
//<body>
//                这是 html页面数据
//                </body>
//</html>
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <servlet>
        <servlet-name>PringHtml</servlet-name>
        <servlet-class>com.atjava.servlet.PringHtml</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>PringHtml</servlet-name>
        <url-pattern>/pringHtml</url-pattern>
    </servlet-mapping>
</web-app>
  • jsp方式:
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/8
  Time: 14:57
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>jsp回传页面</title>
</head>
<body>
这是 jsp页面数据
</body>
</html>

如何访问 jsp: jsp页面和 html页面一样,都是存放在 web目录下,访问方式和 html页面一样;

jsp的本质

jsp页面本质上是一个 servlet程序;

当我们第一次访问 jsp页面时, Tomcat服务器会帮我们把 jsp页面翻译为 java源文件,并且将它编译为 .class字节码程序;

public final class a_jsp extends HttpJspBase {}

我们跟踪源码发现, HttpJspBase类; 它直接继承了 HttpServlet类; 也就是说 jsp翻译出的 java类,间接继承了 HttpServlet类, 也就是说翻译出来的是一个 Servlet程序;

总结: 通过翻译的 java源代码我们就可以得到结果: jsp就是 servlet程序; 通过观察翻译出的 Servlet程序的源码,可以发现,其底层实现也是通过 write(...)输出流,将 html页面数据回传给客户端.

jsp的三种语法

jsp头部的 page指令

jsp的 page指令可以修改 jsp页面中一些重要的属性或行为;

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
  • language: 表示 jsp翻译后是什么语言文件,暂时只支持 java

  • contentType: 表示 jsp返回的数据类型,也是源码中 response.setContentType()的参数值

  • pageEncoding: 表示当前 jsp页面文件本身的字符集

  • import: 跟 java源代码一样,用于导包,导类

  • errorPage: 设置当 jsp页面运行出错,自动跳转去的错误页面路径

  • isErrorPage: 设置当前 jsp页面是否为错误信息页面,默认是 false,如果是 true可以获取异常信息

  • session: 设置访问当前 jsp页面,是否会创建 HttpSession对象,默认是 true

  • extends: 设置 jsp翻译出来的 java类默认继承谁

<%@ page contentType="text/html;charset=UTF-8"
         errorPage="/error500.jsp"
         extends="javax.servlet.HttpServlet"
         language="java" %>

---------------以下两个属性是给 out输出流使用-------------

  • autoFlush: 设置当 out输出流缓冲区满了后,是否自动刷新缓冲区,默认值是 true
  • buffer: 设置 out缓冲区的大小,默认是 8kb
<%@ page import="javax.lang.model.element.VariableElement" %><%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/8
  Time: 15:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8"
         errorPage="/error500.jsp"
         extends="javax.servlet.HttpServlet"
         language="java" %>
<%--
errorPage表示错误后自动跳转去的路径
这个路径一般都是以 ‘/’开始,它表示请求地址为 http://ip:port/工程路径/
映射到代码的 web目录
--%>
<html>
<head>
    <title>jsp</title>
</head>
<body>
    这是 b.jsp页面数据
<%
    int num = 12 / 0;
%>
</body>
</html>
jsp中的常用脚本

声明脚本 (极少使用)

声明脚本的格式: <%! 声明 java代码 %>;

作用: 可以给 jsp翻译出来的 java类定义属性和方法,甚至是 静态代码块,内部类等;

  • 声明类属性
  • 声明 static静态块
  • 声明类方法
  • 声明内部类
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %><%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/8
  Time: 14:57
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>jsp回传页面</title>
</head>
<body>
这是 a.jsp页面数据
<%--
1.声明类属性
2.声明 static静态代码块
3.声明类方法
4.声明内部类
--%>
<%-- 1.声明类属性 --%>
<%!
private Integer id;
private String name;
private static Map<String, Object> map;
%>
<%-- 2.声明 static静态代码块 --%>
<%!
static {
    map = new HashMap<String, Object>();
    map.put("key", "val");
    map.put("key2", "val2");
    map.put("key3", "val3");
}
%>
<%-- 3.声明类方法 --%>
<%!
public int abc () {
    return 12;
}
%>
<%-- 4.声明内部类 --%>
<%!
public static class A {
    private Integer id = 12;
    private String abc = "abc";
}
%>
</body>
</html>

表达式脚本 (常用)

表达式脚本的格式是: <%= 表达式 %>;

表达式脚本的作用: 在 jsp页面上输出数据;

  • 输出整型
  • 输出浮点型
  • 输出字符型
  • 输出对象
<%--
- 输出整型
- 输出浮点型
- 输出字符型
- 输出对象
--%>
<%-- 输出整型 --%>
<br/>
<%=12%><br/>
<%-- 输出浮点型 --%>
<%=12.12%><br/>
<%-- 输出字符型 --%>
<%="中华"%><br/>
<%-- 输出对象 --%>
<%=map%><br/>
<%=request.getParameter("username")%><br/>

表达式脚本的特点:

  1. 所有的表达式脚本都会被翻译到 _jspService()方法中
  2. 表达式脚本都会被翻译成为 out.print(...)输出到页面上
  3. 由于表达式脚本翻译的内容都在 _jspService()方法中,所以 _jspService()中的对象都可以使用
  4. 表达式脚本中的 表达式不能以分号结束

代码脚本

代码脚本的格式: <% java语句 %>;

代码脚本的作用是: 可以在 jsp页面中,编写我们自己需要的功能 (写的是 java语句);

  • 代码脚本 if语句
  • 代码脚本 for循环语句
  • 翻译后 java文件中 _jspService方法内的代码都可以 使用
<%--
- 代码脚本 if语句
- 代码脚本 for循环语句
- 翻译后 java文件中 _jspService方法内的代码都可以写
--%>

<%-- 代码脚本 if语句 --%>
<%
int i = 12;
if (i == 12) {
    System.out.println("中华崛起");
} else {
    System.out.println("崛起中华");
}
%>

<%-- 代码脚本 for循环语句 --%>
<%-- 代码脚本块 --%>
<%
for (int num = 0; num < 10; ++num) {
%>
<%
    System.out.println(num);
}
%>

<%-- 代码脚本和 表达式脚本 组合使用--%>
<table border="1" cellspacing="0" cellpadding="0">
<%
    for (int num = 0; num < 10; ++num) {
%>
    <tr>
        <td>
            第<%=num + 1%>行
        </td>
    </tr>
<%
    }
%>
</>
<%-- 翻译后 java文件中 _jspService方法内的代码都可以写 --%>
<%
    String username = request.getParameter("username");
    System.out.println(username);
%>

代码脚本的特点是:

  1. 代码脚本翻译后都在 _jspService() 方法中
  2. 代码脚本由于翻译到 _jspService() 方法中,所以在 _jspService() 方法中的现有对象都可以直接使用
  3. 可以由多个 代码脚本块 组合完成一个完整的 java语句
  4. 代码脚本可以和表达式脚本一起组合使用,在 jsp页面上输出数据
jsp中的三种注释

html 注释

html注释会被翻译到 java源代码中,在 _jspService方法里,以 out.write()输出到客户端

<!-- 这是 html注释 -->

java 注释

<%
    // 单行注释
    /* 多行注释 */
%>

jsp 注释

  • 注:jsp注释可以注释一切
<%-- 这是 jsp注释 --%>

jsp 九大内置对象

jsp中的内置对象,是指 Tomcat在翻译 jsp页面成为 Servle源代码后,内部提供的九大对象,叫内置对象。

名称描述
request请求对象
response响应对象
pageContextjsp的上下文对象
session会话对象
applicationServletContext对象
configServletConfig对象
outjsp输出流对象
page指向当前 jsp的对象
exception异常对象(isErrorPage="true")

jsp 四大域对象

域对象是可以像 Map一样存取数据的对象;四个域对象功能都一样,不同的是它们对数据的存取范围。

虽然四个域对象都可以存取数据,但在它们的使用上是由优先顺序的;四个域的优先顺序分别是,它们从小到大的范围顺序(优化内存)。

pageContext -> request -> session -> application
  • pageContext:(PageContextImpl类),当前 jsp页面范围内有效,跳出当前也米娜后无效
  • request:(HttpServletRequest类),一次请求内有效
  • session:(HttpSession类),一个会话范围内有效(从浏览器访问服务器开始,直到浏览器关闭结束)
  • application:(ServletContext类),整个 web工程范围内有效(只要 web工程不停止,数据都有效)
// scope.jsp
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/10
  Time: 22:37
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>域对象</title>
</head>
<body>
    <h2>scope.jsp页面</h2>
    <%
        // 四个域中都分别保存数据
        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>
</html>
// scope2.jsp
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/10
  Time: 22:37
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>域对象</title>
</head>
<body>
    <h2>scope2.jsp页面</h2>

    pageContext 域是否有值:<%=pageContext.getAttribute("key")%><br/>
    request 域是否有值:<%=request.getAttribute("key")%><br/>
    session 域是否有值:<%=session.getAttribute("key")%><br/>
    application 域是否有值:<%=application.getAttribute("key")%><br/>

</body>
</html>

jsp中的 out输出和 response.getWriter输出的区别

jsp中的代码执行时,内存中会有 out缓冲区和 response缓冲区,当 jsp页面中的所有代码执行完成后会做以下两个操作:

  1. 执行 out.flush()操作,将 out缓冲区中的数据追加到 response缓冲区末尾
  2. 执行 response的刷新操作,将全部数据写给客户端

由于 jsp翻译后,底层源码都是使用 out来进行输出,所以一般情况下,我们在 jsp页面中统一使用 out.write()输出,避免打乱页面输出内容的顺序。

  1. out.write() 输出字符串没有问题,只适合输出字符串
  2. out.print() 输出任意数据都没有问题(都转换成为字符串后调用 write输出)
(char)num; // 将 num 转换为 ASCII字符,后输出
  • 结论:在 jsp页面中,可以统一使用 out.print()进行输出

jsp 的常用标签

jsp 静态包含(推荐)

页面中共同的部分做成 "组件",其它页面统一引入,有修改时只修改一个 "组件" 即可

  • 语法格式:<%@ include file=""%>

  • file属性指定你要包含的 jsp页面的路径

  • 地址中第一个为 "/" 表示为 http://ip:port/工程路径/ 映射到代码的 web目录

  • main.jsp

<%--
C:\Users\Weili\.IntelliJIdea2018.1\system\tomcat\Tomcat_8_0_50_JavaWeb_5\work\Catalina\localhost\ROOT\org\apache\jsp\include
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/11
  Time: 22:31
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>main</title>
</head>
<body>
    头部信息<br/>
    主体内容<br/>
    <%--页脚信息<br/> 将页脚部分抽离出来,使用静态包含方法--%>
    <%--
        <%@ include file=""%> 这就是静态包含语法,类似于组件的引用
        file属性指定你要包含的 jsp页面的路径
        地址中第一个为 "/" 表示为 http://ip:port/工程路径/ 映射到代码的 web目录
    --%>
    <%@ include file="/include/footer.jsp" %>
</body>
</html>
  • footer.jsp
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/11
  Time: 22:34
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>footer</title>
</head>
<body>
    页脚信息<br/>
</body>
</html>
  • 特点:
    1. 静态包含不会编译被包含的 jsp页面
    2. 静态包含只是将被包含的 jsp页面的代码拷贝到包含的位置执行输出

jsp 动态包含

  • 语法格式:
<jsp:include page="/...">
    <jsp:param name="xxx" value="xxx"></jsp:param>
</jsp:include>
  • page:属性是指定你要包含的 jsp页面的路径
  • 动态包含也可以像 静态包含一样,将被包含的内容执行输出到包含位置
  • main.jsp
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/11
  Time: 22:31
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>main</title>
</head>
<body>
    头部信息<br/>
    主体内容<br/>
    <%--页脚信息<br/> 将页脚部分抽离出来,使用静态包含方法--%>
    <%--
        <%@ include file="/..."%> 这就是静态包含语法,类似于组件的引用
        file属性指定你要包含的 jsp页面的路径
        地址中第一个为 "/" 表示为 http://ip:port/工程路径/ 映射到代码的 web目录
    --%>
    <%--<%@ include file="/include/footer.jsp" %>--%>

    <%--
        <jsp:include page="/...">
            <jsp:param name="xxx" value="xxx"></jsp:param>
        </jsp:include>
        page:属性是指定你要包含的 jsp页面的路径
        动态包含也可以像 静态包含一样,将被包含的内容执行输出到包含位置
    --%>
    <jsp:include page="/include/footer.jsp">
        <jsp:param name="username" value="root"></jsp:param>
    </jsp:include>
</body>
</html>
  • footer.jsp
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/11
  Time: 22:34
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>footer</title>
</head>
<body>
    页脚信息<br/>
    获取参数:<%=request.getParameter("username")%>
</body>
</html>
  • 动态包含的底层原理: jsp动态包含图解.png

  • 特点:

    1. 动态包含会把包含的 jsp页面也翻译成为 java代码
    2. 动态包含底层代码使用如下代码去调用被包含的 jsp页面执行输出(静态包含是将被引用的页面完全复制过来输出):
    org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false);
    
    1. 动态包含,还可以传递参数

jsp 请求转发

  • 语法格式:<jsp:forward page="/..."></jsp:forward>
  • page表示转发的内容
<%--
    请求转发,方式一:
--%>
<%--<%--%>
    <%--request.getRequestDispatcher("/scope2.jsp").forward(request, response);--%>
<%--%>--%>
<%--
    请求转发,方式二:使用标签
    <jsp:forward page="/..."></jsp:forward>
--%>
<jsp:forward page="/scope.jsp"></jsp:forward>

请求转发的具体使用:

一、 客户端:

  • 将搜索关键词发送给 SearchServlet程序

二、 服务端:

  1. 获取请求中参数:关键字
  2. 发送 sql请求,查询有关数据
  3. 保存 sql返回的结果,保存到 request域中
  4. 请求转发

三、jsp页面:

  • 获取 request域中的数据,返回到客户端

实例:

  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>SearchStudentServlet</servlet-name>
        <servlet-class>com.atjava.servlet.SearchStudentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SearchStudentServlet</servlet-name>
        <url-pattern>/sss</url-pattern>
    </servlet-mapping>
</web-app>
  • SearchStudentServlet.java
package com.atjava.servlet;

import com.atjava.pojo.Student;

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.util.ArrayList;
import java.util.List;

/**
 * @author lv
 * @create 2021-08-16 21:54
 */
public class SearchStudentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doGet(req, resp);
        // 1.获取请求参数
        String keywords = req.getParameter("keywords");
        System.out.println("keywords: " + keywords);
        // 2.发 sql语句,查询信息
        // 模拟 sql请求结果
        List<Student> studentList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            int a = i + 1;
            studentList.add(new Student(a, "vv-" + a, a + 10, "phone-" + a));
        }
        // 3.保存查询到的结果,放到 request中
        req.setAttribute("stuList", studentList);
        // 4.请求转发
        req.getRequestDispatcher("/include/showStudent.jsp").forward(req, resp);
    }
}
  • showStudent.jsp
<%@ page import="java.util.List" %>
<%@ page import="com.atjava.pojo.Student" %>
<%@ page import="java.util.ArrayList" %>
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/16
  Time: 20:45
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>student-info</title>
    <style>
        table {
            width: 560px;
            border: 1px #f0f solid;
            border-collapse: collapse;
        }
        tr, td {
            border: 1px #000 solid;
        }
    </style>
</head>
<body>
    <%--
        请求转发的具体使用:

        客户端:
            将搜索关键词发送给 SearchServlet程序

        服务端:
            1.获取请求中参数:关键字
            2.发送 sql请求,查询有关数据
            3.保存 sql返回的结果,保存到 request域中
            4.请求转发

        jsp页面:
            获取 request域中的数据,返回到客户端
    --%>
    <%--輸出一個表格,包含10個學生的信息--%>
    <%
        List<Student> studentList = (ArrayList) request.getAttribute("stuList");
//        for (int i = 0; i < 10; i++) {
//            int a = i + 1;
//            studentList.add(new Student(a, "lv-" + a, a + 10, "phone-" + a));
//        }
    %>

    <table>
        <tr>
            <td>id:</td>
            <td>name:</td>
            <td>age:</td>
            <td>phone:</td>
        </tr>
        <%
            for (Student student : studentList) {
        %>
            <tr>
                <td><%=student.getId()%></td>
                <td><%=student.getName()%></td>
                <td><%=student.getAge()%></td>
                <td><%=student.getPhone()%></td>
            </tr>
        <%
            }
        %>
    </table>
</body>
</html>

练习

  • 输出乘法口诀:
<%--jsp
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/12
  Time: 22:19
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>九九乘法表</title>
</head>
<body>
    <h2>九九乘法表:</h2>
    <%--<table border cellpadding="4" cellspacing="0">--%>
    <table>

        <%
            for (int i = 1; i <= 9; ++i) {
        %>
                <tr>
        <%
                for (int j = 1; j <= i; ++j) {
        %>
                    <td>
                        <%--<%=i%> * <%=j%> = <%=i*j%>--%>
                        <%= i + " X " + j + " = " + (i*j)%>
                    </td>
        <%
                }
        %>
                </tr>
        <%
            }
        %>
    </table>
</body>
</html>
  • 向客户端输出一个包含 10学生的表格
<%@ page import="java.util.List" %>
<%@ page import="com.atjava.pojo.Student" %>
<%@ page import="java.util.ArrayList" %>
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/16
  Time: 20:45
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>student-info</title>
    <style>
        table {
            width: 560px;
            border: 1px #f0f solid;
            border-collapse: collapse;
        }
        tr, td {
            border: 1px #000 solid;
        }
    </style>
</head>
<body>
    <%--
        请求转发的具体使用:

        客户端:
            将搜索关键词发送给 SearchServlet程序

        服务端:
            1.获取请求中参数:关键字
            2.发送 sql请求,查询有关数据
            3.保存 sql返回的结果,保存到 request域中
            4.请求转发

        jsp页面:
            获取 request域中的数据,返回到客户端
    --%>
    <%--輸出一個表格,包含10個學生的信息--%>
    <%
        List<Student> studentList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            int a = i + 1;
            studentList.add(new Student(a, "lv-" + a, a + 10, "phone-" + a));
        }
    %>

    <table>
        <tr>
            <td>id:</td>
            <td>name:</td>
            <td>age:</td>
            <td>phone:</td>
        </tr>
        <%
            for (Student student : studentList) {
        %>
            <tr>
                <td><%=student.getId()%></td>
                <td><%=student.getName()%></td>
                <td><%=student.getAge()%></td>
                <td><%=student.getPhone()%></td>
            </tr>
        <%
            }
        %>
    </table>
</body>
</html>

Listener监听器

  1. Listener监听器是 JavaWeb的三大组件之一;

  2. JavaWeb三大组件分别是: Servlet程序, Filter过滤器, Listener监听器;

  3. 其中 Listener就是 JavaEE的规范, 就是接口;

  4. 监听器的作用是, 监听某种事物的变化, 然后通过回调函数,反馈给客户 (程序)去做相应的处理;

  5. 监听器一共有八个, 现在有用的只有 ServletContextListener监听器还在使用

ServletContextListener 监听器

ServletContextListener它可以监听 ServletContext对象的创建和销毁;

servletContext对象在 web工程启动时创建,在 web工程停止时销毁;

监听到创建和销毁后都会分别调用 ServletContextListener监听器的方法反馈;

这两个方法分别是:

  1. public void contextInitialized(ServletContextEvent sce)

    • 在 ServletContext 对象创建后马上调用,做初始化
  2. public void contextDestroyed(ServletContextEvent sce)

    • 在 ServletContext对象销毁之后调用

使用 ServletContextListener监听器监听 ServletContext对象, 使用步骤如下:

  1. 编写接口实现类去实现 ServletContextListener接口
  2. 实现 创建 和 销毁回调方法
package com.atjava.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * @author lv
 * @create 2021-08-17 21:37
 */
public class MyServletContextListenerImpl implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext Object init");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext Object destroyed");
    }
}
  1. 到 web.xml 中去配置监听器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>SearchStudentServlet</servlet-name>
        <servlet-class>com.atjava.servlet.SearchStudentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SearchStudentServlet</servlet-name>
        <url-pattern>/sss</url-pattern>
    </servlet-mapping>

    <!-- 配置监听器 -->
    <listener>
        <listener-class>com.atjava.listener.MyServletContextListenerImpl</listener-class>
    </listener>
</web-app>

EL表达式 & JSTL标签库

EL表达式

EL表达式的全称是:Expression Language 表达式语言

  • EL表达式主要是为了替换 jsp中的表达式脚本

EL表达式及其作用:

EL表达式主要是代替 jsp页面中的表达式脚本在 jsp页面中进行数据的输出,因为 EL表达式在输出数据时,要比 jsp的表达式脚本要简洁很多。

  • EL表达式的格式是:${key}
  • EL表达式在输出 null值时,输出的是空串;jsp表达式脚本输出 null值时,不做处理输出 null值
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/18
  Time: 21:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>testEl</title>
</head>
<body>
    <%
        request.setAttribute("key", "EL");
    %>
    表达式脚本输出 key值:<%=request.getAttribute("key") == null ? "" : request.getAttribute("key")%><br/>
    <%--
        1.EL表达式对于没有值的处理是:输出空值,而表达式脚本输出的是 null
    --%>
    EL表达式输出 key值:${key}
</body>
</html>

EL表达式搜索域数据的顺序:

EL表达式主要是在 jsp页面中输出数据;其主要是输出域对象中的数据。

当四个域(pageContext、request、session、application)中都有相同的 key名时,EL表达式会按照四个域从小到大的顺序去搜索,找到后就输出,和代码书写的先后顺序无关。

<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/18
  Time: 22:11
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>testEL2</title>
</head>
<body>
    <%
        pageContext.setAttribute("key", "pageContext"); // 当前页面
        request.setAttribute("key", "request"); // 一次请求
        session.setAttribute("key", "session"); // session 浏览器关闭后
        application.setAttribute("key", "application"); // 服务关闭
    %>

    who is priority:${key}
</body>
</html>

EL表达式输出 Bean的普通属性 & 数组属性 & List集合属性 & Map集合属性

注意:EL实际上获取的是 Bean中属性对应的 getXxx方法,没有 getXxx方法就无法获取 Bean中对象的属性。

<%@ page import="com.atjava.pojo.Person" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %><%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/18
  Time: 22:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>PersonInfo</title>
</head>
<body>
    <%
        Person p1 = new Person();
        p1.setName("wvv");
        p1.setAge(12);
        p1.setPhones(new String[]{"15666002221", "4512552155", "64513131321"});
        List<String> arrayList = new ArrayList<>();
        arrayList.add("车夹装");
        arrayList.add("贵腚");
        p1.setCities(arrayList);
        Map<String, Object> map = new HashMap<>();
        map.put("key", "person");
        map.put("des", "beautifual");
        p1.setMap(map);

        pageContext.setAttribute("person", p1);
    %>

    输出 Person全信息(toString):${person}<br/>

    输出普通属性 name:${person.name}<br/>
    输出普通属性 age:${person.age}<br/>

    输出 Array属性中的一个元素:${person.phones[0]}<br/>

    输出 List属性:${person.cities}<br/>
    输出 List属性的一个元素:${person.cities[1]}<br/>

    输出 Map属性:${person.map}<br/>
    输出 Map属性的一个键值:${person.map.key}<br/>
</body>
</html>
package com.atjava.pojo;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author lv
 * @create 2021-08-18 22:29
 */
public class Person {
    private String name = "vv";
    private int age;

    private String[] phones;
    private List<String> cities;
    private Map<String, Object> map;

    public Person() {
    }

    public Person(String name, int age, String[] phones, List<String> cities, Map<String, Object> map) {
        this.name = name;
        this.age = age;
        this.phones = phones;
        this.cities = cities;
        this.map = map;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String[] getPhones() {
        return phones;
    }

    public void setPhones(String[] phones) {
        this.phones = phones;
    }

    public List<String> getCities() {
        return cities;
    }

    public void setCities(List<String> cities) {
        this.cities = cities;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", phones=" + Arrays.toString(phones) +
                ", cities=" + cities +
                ", map=" + map +
                '}';
    }
}

EL表达式 - 运算

  • 语法: ${ 运算表达式 }, EL表达式支持如下运算:

关系运算:

关系运算符说明范例结果
== 或 eq等于${ 6==6 }, ${ 6 eq 6 }true
!= 或 ne不等于${ 4!=6 }, ${ 4 ne 6 }false
< 或 lt小于${ 3<6 }, ${ 3 lt 6 }true
> 或 gt大于${ 5>6 }, ${ 5 gt 6 }false
<= 或 le小于等于${ 5<=6 }, ${ 5 le 6 }true
>= 或 ge大于等于${ 3>=6 }, ${ 3 ge 6 }false

逻辑运算:

逻辑运算符说明范例结果
&& 或 and与运算${ 6==6 && 5 > 6 }, ${ 6==6 and 5 > 6 }false
或 or或运算${ 6==65 > 6 }, ${ 6==6 or 5 > 6 }true
! 或 not取反运算${ !true }, ${ not true }false

算数运算:

算数运算符说明范例结果
+加法${ 12 + 6 }18
-减法${ 12 - 6 }6
*乘法${ 12 * 6 }72
/ 或 div除法12/6{ 12 / 6 } 或 { 12 div 6 }2
% 或 mod取模${ 144 % 10 } 或 ${ 144 mod 10 }4

empty 运算

empty运算可以判断一个数据是否为空, 如果为空输出 true, 反之输出 false

  • 值为 null值时,为空
  • 值为空串时, 为空
  • 值是 Object类型数组, 长度为零时, 为空
  • List集合, 元素个数为零, 为空
  • map集合, 元素个数为零, 为空
<%
    request.setAttribute("emptyNull", null);
    request.setAttribute("emptyStr", "");
    request.setAttribute("emptyList", new ArrayList<String>());
    request.setAttribute("emptyObj", new Object[]{});
    request.setAttribute("emptyMap", new HashMap<String, Object>());
%>
emptyNull:${ empty emptyNull }<br/>
emptyStr:${ empty emptyStr }<br/>
emptyList:${ empty emptyList }<br/>
emptyObj:${ empty emptyObj }<br/>
emptyMap:${ empty emptyMap }<br/>

三元运算

  • 语法: ${ 表达式1 ? 表达式2 : 表达式3 }
三元表达式:${ 12 == 12 ? "等于" : "不等于"}<br/>

"." 运算符 和 "[]"中括号 运算符

  • 点运算,可以输出 Bean对象中某个属性的值
  • 中括号运算, 可以输出 Bean对象中某个数组元素的值
  • 并且 []括号运算,可以输出 map集合中 key中含有特殊字符的 key值
<%
    request.setAttribute("emptyNull", null);
    request.setAttribute("emptyStr", "");
    List<String> list = new ArrayList<>();
    request.setAttribute("emptyList", list);
    list.add("list1");
    request.setAttribute("emptyObj", new Object[]{});
    Map<String, Object> map =  new HashMap<String, Object>();
    map.put("k1", "map1");
    map.put("k==y", "map2");
    request.setAttribute("emptyMap", map);
%>
emptyNull:${ empty emptyNull }<br/>
emptyStr:${ empty emptyStr }<br/>
emptyList:${ empty emptyList }<br/>
emptyObj:${ empty emptyObj }<br/>
emptyMap:${ empty emptyMap }<br/>

三元表达式:${ 12 == 12 ? "等于" : "不等于"}<br/>

点运算:${emptyMap.k1}<br/>
点运算(k==y):${emptyMap["k==y"]}<br/>
中括号运算:${emptyList[0]}<br/>

<%--
    三元表达式:等于
    点运算:map1
    点运算(k==y):map2
    中括号运算:list1
--%>

EL表达式的 11个隐含对象

EL表达式中 11个隐含对象,是 EL表达式中自己定义的,可以直接使用。

变量类型作用
pageContextPageContextImpl它可以获取 jsp中的 九大内置对象
pageScopeMap<String, Object>它可以获取 pageContext域中的数据
requestScopeMap<String, Object>它可以获取 request域中的数据
sessionScopeMap<String, Object>它可以获取 session域中的数据
applicationScopeMap<String, Object>它可以获取 servletContext域中的数据
paramMap<String, String>它可以获取请求参数的值
paramsValuesMap<String, String[]>它也可以获取请求参数的值,在获取多个值时使用
headerMap<String, String>它可以获取请求头的信息
headerValuesMap<String, String[]>它也可以获取请求头的信息,可以获取多个值的情况
cookieMap<String, Cookie>它可以获取当前请求的 Cookie信息
initParamMap<String, String>它可以获取在 web.xml中配置的 上下文参数<context-param>

EL获取四个特定域中的属性

  • pageScope:pageContext域
  • requestScope:Request域
  • sessionScope:Session域
  • applicationScope:ServletContext域
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/22
  Time: 21:43
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>scope</title>
</head>
<body>
    <%
        pageContext.setAttribute("key1", "value1");
        pageContext.setAttribute("key2", "value2");
        request.setAttribute("key1", "value1");
        session.setAttribute("key1", "value1");
        application.setAttribute("key1", "value1");
    %>

    pageScope: ${pageScope.key1}<br/>
    requestScope: ${requestScope.key1}<br/>
    sessionScope: ${sessionScope.key1}<br/>
    applicationScope: ${applicationScope.key1}<br/>

</body>
</html>

pageContext对象的使用

  • 协议:
  • 服务器 ip:
  • 服务器端口号:
  • 获取工程路径:
  • 获取请求方法:
  • 获取客户端 ip地址:
  • 获取会话的 id编号:
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/22
  Time: 21:55
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>pageContext</title>
</head>
<body>
    ${pageContext} <%-- org.apache.jasper.runtime.PageContextImpl@5447dff4 --%>

    <%--- 协议:--%>
    jsp协议:<%=request.getScheme()%><br/>
    EL协议:${pageContext.request.scheme}<br/>
    <%--- 服务器 ip:--%>
    jsp服务器 ip:<%=request.getServerName()%><br/>
    EL服务器 ip:${pageContext.request.serverName}<br/>
    <%--- 服务器端口号:--%>
    jsp服务器端口号:<%=request.getServerPort()%><br/>
    EL服务器端口号:${pageContext.request.serverPort}<br/>
    <%--- 获取工程路径:--%>
    jsp获取工程路径:<%=request.getContextPath()%><br/>
    EL获取工程路径:${pageContext.request.contextPath}<br/>
    <%--- 获取请求方法:--%>
    jsp获取请求方法:<%=request.getMethod()%><br/>
    EL获取请求方法:${pageContext.request.method}<br/>
    <%--- 获取客户端 ip地址:--%>
    jsp获取客户端 ip地址:<%=request.getRemoteHost()%><br/>
    EL获取客户端 ip地址:${pageContext.request.remoteHost}<br/>
    <%--- 获取会话的 id编号:--%>
    jsp获取会话的 id编号:<%=session.getId()%><br/>
    EL获取会话的 id编号:${pageContext.session.id}<br/>

    <%--
        实际开发中 pageContext的使用技巧:
    --%>
    <%
        pageContext.setAttribute("req", request);
    %>
    pageContext的使用技巧:${req.serverName}<br/>
    <%--
        EL协议:http
        jsp服务器 ip:localhost
        EL服务器 ip:localhost
        jsp服务器端口号:8080
        EL服务器端口号:8080
        jsp获取工程路径:
        EL获取工程路径:
        jsp获取请求方法:GET
        EL获取请求方法:GET
        jsp获取客户端 ip地址:127.0.0.1
        EL获取客户端 ip地址:127.0.0.1
        jsp获取会话的 id编号:5FFE428A14CB6089A690DDF6B5E26FDF
        EL获取会话的 id编号:5FFE428A14CB6089A690DDF6B5E26FDF
    --%>

</body>
</html>

pageContext在实际开发中使用的技巧

    <%--
        实际开发中 pageContext的使用技巧:
    --%>
    <%
        pageContext.setAttribute("req", request);
    %>
    pageContext的使用技巧:${req.serverName}<br/>

EL表达式其它隐含对象的使用

<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/23
  Time: 20:49
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>other</title>
</head>
<body>
    <%-- param:请求的参数,Map类型--%>
    ${param}<br/>
    ${paramValues}<br/>
    param 输出请求参数 username:${param.username}<br/>
    param 输出请求参数 password:${param.password}<br/>
    paramValues 输出请求参数 username:${paramValues.username[0]}<br/>
    paramValues 输出请求参数 password:${paramValues.password[0]}<br/>
    paramValues 输出请求参数 hobby:${paramValues.hobby[0]}<br/>
    paramValues 输出请求参数 hobby:${paramValues.hobby[1]}<br/>
    <%--
        {password=456, username=123, hobby=java}
        {password=[Ljava.lang.String;@531609fc, username=[Ljava.lang.String;@164293d5, hobby=[Ljava.lang.String;@3c64cd8}
        param 输出请求参数 username:123
        param 输出请求参数 password:456
        paramValues 输出请求参数 username:123
        paramValues 输出请求参数 password:456
        paramValues 输出请求参数 hobby:java
        paramValues 输出请求参数 hobby:cpp
    --%>

    <%-- header:获取请求头的信息 --%>
    ${header}<br/>
    User-Agent:${header["User-Agent"]}<br/>
    Connection:${header.Connection}<br/>
    User-Agent:${headerValues["User-Agent"][0]}<br/>
    <%--
        User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
        Connection:keep-alive
        User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
    --%>
    <br/>
    ${cookie}<br/>
    JSESSIONID:${cookie.JSESSIONID}<br/>
    获取 cookie的名称:${cookie.JSESSIONID.name}<br/>
    获取 cookie的值:${cookie.JSESSIONID.value}<br/>
    <%--
        {JSESSIONID=javax.servlet.http.Cookie@4a346f24, Idea-6b7e3bf7=javax.servlet.http.Cookie@1bbfc951}
        JSESSIONID:javax.servlet.http.Cookie@4a346f24
        获取 cookie的名称:JSESSIONID
        获取 cookie的值:404BD160A60F46130F80E087A1B88CE1
    --%>

    <br/>
    <%-- 修改配置文件后一定要重新部署才能生效 --%>
    ${initParam}<br/>
    输出 context-param的值 username:${initParam.username}<br/>
    输出 context-param的值 url:${initParam.url}<br/>
    <%--
        {password=root123, initParam=Map, url=jdbc:mysql//localhost:3306/test, username=root}
        输出 context-param的值 username:root
        输出 context-param的值 url:jdbc:mysql://localhost:3306/test
    --%>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>initParam</param-name>
        <param-value>Map</param-value>
    </context-param>
    <context-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
    </context-param>
    <context-param>
        <param-name>password</param-name>
        <param-value>root123</param-value>
    </context-param>
    <context-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/test</param-value>
    </context-param>
</web-app>

JSTL 标签库(次重点)

  • JSTL标签库主要是为了替换代码脚本

JSTL标签库,全称是指 JSP Standard Tag Library,JSP标准标签库,是一个不断完善的开放源代码的 JSP标签库。

EL表达式主要是为了替换 jsp中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个 jsp 页面变得更加简洁。

JSTL由五个不同功能的标签库组成:

功能范围uri前缀
核心标签库(重点java.sun.com/jsp/jstl/co…c
格式化java.sun.com/jsp/jstl/fm…fmt
函数java.sun.com/jsp/jstl/fu…fn
数据库(不使用)java.sun.com/jsp/jstl/sq…sql
XML(不使用)java.sun.com/jsp/jstl/xm…x

在 jsp页面中使用 taglib指令引入标签库:

// core标签库
<%@ taglib prefix="c"uri="http://java.sun.com/jsp/jstl/core" %>
// xml标签库
<%@ taglib prefix="x"uri="http://java.sun.com/jsp/jstl/xml" %>
// fmt标签库
<%@ taglib prefix="fmt"uri="http://java.sun.com/jsp/jstl/fmt" %>
// sql标签库
<%@ taglib prefix="sql"uri="http://java.sun.com/jsp/jstl/sql" %>
// functions标签库
<%@ taglib prefix="fn"uri="http://java.sun.com/jsp/jstl/functions" %>

JSTL标签库的使用步骤:

  1. 先导入 jstl标签库的 jar包:使用右击选择 add as Library... 配置 jar包
    taglibs-standard-impl-1.2.1.jar
    taglibs-standard-spec-1.2.1.jar
    
  2. 使用 taglib指令 导入用到的标签库
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    

core 核心库的使用

<c:set />

  • set标签可以向域中保存数据
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/23
  Time: 22:39
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>core</title>
</head>
<body>
    <%--
        <c:set />:向域中保存数据

        scopeObj.setAttribute(key, value);

        scope 属性值表示保存到的域对象
        var 属性值设置 key属性名
        value属性值 设置属性值
    --%>
    set之前:${pageScope.key1 ? pageScope.key1 : "无"}<br/>
    
    <c:set scope="page" var="key1" value="abc123" />
    
    set之后:${pageScope.key1}<br/>
    <%--
        set之前:无
        set之后:abc123
    --%>
</body>
</html>

<c:if />

  • if 标签用来做 if判断
<%--
    <c:if />:if 标签用来做 if判断

    test属性值表示判断的条件(使用 EL表达式)
--%>
<c:if test="${ 12 == 12}">
    <%--if判断成立,执行内部的代码--%>
    <h2>if条件成立</h2>
</c:if>

<c:choose><c:when><c:otherwise> 标签

  • 多路判断,与 switch ... case ... default 类似
  • 注意:
    1. 标签中是能使用 jsp注释
    2. when标签的父标签一定是 choose 标签
<%--
    <c:choose><c:when><c:otherwise> 标签:多路判断

    choose标签:开始选择判断
    when标签:表示每种判断情况
        test属性值表示当前判断的值
    otherwise标签:条件都不符合时的默认情况
--%>
<%
    request.setAttribute("height", 172);
%>
<c:choose>
    <c:when test="${requestScope.height > 190}">
        <h3>小巨人</h3>
    </c:when>
    <c:when test="${requestScope.height > 180}">
        <h3>高人</h3>
    </c:when>
    <c:when test="${requestScope.height > 170}">
        <h3>jental man</h3>
    </c:when>
    <c:otherwise>
        <c:choose>
            <c:when test="${requestScope.height > 160}">
                <h3>大于 160</h3>
            </c:when>
            <c:when test="${requestScope.height > 150}">
                <h3>大于 150</h3>
            </c:when>
            <c:when test="${requestScope.height > 140}">
                <h3>大于 140</h3>
            </c:when>
        </c:choose>
    </c:otherwise>
</c:choose>

<c:forEach />

  • 遍历输出数据
  1. 遍历数据 1 - 10,输出
  • begin设置 开始索引
  • end设置 结束索引
  • var表示循环中的变量(当前正在遍历的数据)
<%--
    1. 遍历数据 1 - 10,输出
    begin设置 开始索引
    end设置 结束索引
    var表示循环中的变量(当前正在遍历的数据)
--%>
<ul>
    <c:forEach var="i" begin="1" end="10">
        <li>
            ${i}
        </li>
    </c:forEach>
</ul>
  1. 遍历 Object数组
  • items 表示遍历的数据源(遍历的集合)
  • var 表示当前遍历到的数据
<%--
    2. 遍历 Object数组
    for (obj : arr)
    items 表示遍历的数据源(遍历的集合)
    var 表示当前遍历到的数据
--%>
<%
    request.setAttribute("strArr", new String[]{"123", "456", "789"});
%>
<table border="1">
    <c:forEach var="item" items="${requestScope.strArr}">
        <tr>
            <td>
                ${item}
            </td>
        </tr>
    </c:forEach>
</table>
  1. 遍历 List集合中的对象 Person,有属性:编号、用户名、密码、年龄、电话
  • begin 表示遍历的开始索引值
  • end 表示结束的索引值
  • step 表示步长值,默认为 1
  • varStatus 表示当前遍历到的数据状态,JSTL内部类:LoopTagStatus
    public interface LoopTagStatus {
        public Object getCurrent(); 表示当前遍历到的数据
        public int getIndex(); 表示获取遍历的索引
        public int getCount(); 表示当前遍历的个数 123 ...
        public boolean isFirst(); 表示当前遍历到的数据是否为第一条
        public boolean isLast(); 表示当前遍历到的数据是否为最后一条
        public Integer getBegin(); 获取 设置的 begin属性值
        public Integer getEnd(); 获取 设置的 end属性值
        public Integer getStep(); 获取 设置的 step属性值
    }
    
  • 实例:
<%@ page import="com.atjava.pojo.Student" %>
// ...
<%--
    3. 遍历 List集合 中存放的 person类,属性:编号、用户名、密码、年龄、电话信息
--%>
<%
    List<Student> list = new ArrayList<>();
    list.add(new Student(1001, "vv", "123", 12, "15620354698"));
    list.add(new Student(1002, "w", "456", 21, "15620794698"));
    list.add(new Student(1003, "vv1", "789", 22, "15642354698"));
    list.add(new Student(1004, "vv2", "321", 20, "15646354698"));
    pageContext.setAttribute("stuList", list);
%>

<table border="1">
    <tr>
        <td>编号:</td>
        <td>用户名:</td>
        <td>密码:</td>
        <td>年龄:</td>
        <td>电话信息:</td>
    </tr>

    <%--
        begin 表示遍历的开始索引值
        end 表示结束的索引值
        step 表示步长值,默认为 1
        varStatus 表示当前遍历到的数据状态,JSTL内部类:LoopTagStatus
    --%>
    <c:forEach begin="1" end="2" varStatus="status" var="item" items="${pageScope.stuList}">
        <tr>
            <td>${item.id}</td>
            <td>${item.username}</td>
            <td>${item.password}</td>
            <td>${item.age}</td>
            <td>${item.phone}</td>
            <td>${status.index}</td>
        </tr>
    </c:forEach>
</table>
  1. 遍历 Map集合
<%--
    4. 遍历 Map集合
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        entry.getKey()
        entry.getValue()
    }
--%>
<%
    Map<String, Object> map = new HashMap<>();
    map.put("key1", 123);
    map.put("key2", "value2");
    pageContext.setAttribute("map", map);
%>
<table border="1">
<c:forEach var="entry" items="${pageScope.map}">
    <tr>
        <td>${entry.key}:</td>
        <td> ${entry.value}</td>
    </tr>
</c:forEach>
</table>

文件的上传和下载

文件的上传介绍

  1. 要有一个 form标签method=post请求
  2. form标签的 encType属性值必须为 multipart/form-data值
  3. 在 form标签中使用 input标签 type=file添加上传的文件
  4. 编写服务器代码(Servlet程序)接收,处理上传的数据
  • encType = multipart/form-data表示提交的数据,以多段(一个表单项对应一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
  • Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryA0Z1si2nrDg9uUnb 表示提交的数据类型 及数据分段的分隔符
    1. boundary=----WebKitFormBoundaryA0Z1si2nrDg9uUnb 的值是每次提交时随机生成的,它就是每段数据的分隔符
    2. 每个分隔符都表示一段数据的开始
    3. 分隔符的值后面多了两个减号,表示上传数据的结束

commons-fileupload.jar常用 API介绍

  • ServletFileUpload类,用于解析上传的数据

  • isMultipartContent() 判断当前上传的数据格式是否是多端格式

  • FileItem类,表示每一个表单项

  • parseRequest() 解析上传的数据

  • boolean FileItem.isFormField() 判断当前这个表单项,是否为普通表单项,true表示普通类型的表单项,false表示上传的文件类型

  • String FileItem.getFieldName() 获取当前表单项的 name 值

  • String FileItem.getString() 获取当前表单项的值

  • String FileItem.getName() 获取上传的文件名

  • void FileItem.write(file) 将上传的文件写到 参数 file所指向的磁盘位置

boolean ServletFileUpload.isMultipartContent(HttpServletRequest request)

public List<FileItem> parseRequest(HttpServletRequest request)

boolean FileItem.isFormField()

String FileItem.getFieldName()

String FileItem.getString()

String FileItem.getName()

void FileItem.write(file)

文件上传实例:

  • UploadServlet.java
package com.atjava.servlet;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @author lv
 * @create 2021-08-26 21:45
 */
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doPost(req, resp);
//        System.out.println("收到 upload");
//        ServletInputStream inputStream = req.getInputStream();
//        byte[] buffer = new byte[102400];
//        int read = inputStream.read(buffer);
//        System.out.println(new String(buffer, 0, read));

//        req.setCharacterEncoding("UTF-8");
        if (ServletFileUpload.isMultipartContent(req)) {

            // 创建 FileItemFactory工厂实现类
            FileItemFactory fif = new DiskFileItemFactory();
            // 创建用于解析上传数据的工具类 ServletFileUpload
            ServletFileUpload sfu = new ServletFileUpload(fif);

            // 解析上传的数据,得到每一个表单项
            try {
                List<FileItem> list = sfu.parseRequest(req);

                // 判断每个表单项的内容
                for (FileItem fileItem : list) {
                    if (fileItem.isFormField()) {
                        // 普通表单项
                        System.out.println("上传表单项的 name:" + fileItem.getFieldName());
                        // 参数 "UTF-8" 解决乱码问题
                        System.out.println("上传表单项的 value:" + fileItem.getString("UTF-8"));
                    } else {
                        // 上传文件
                        System.out.println("上传表单项的 name:" + fileItem.getFieldName());
//                        System.out.println("上传文件的 name:" + fileItem.getName());
                        fileItem.write(new File("F:\Java\java_web\91909529\216\" + fileItem.getName()));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("done");
            }
        }
    }
}
  • upload.jsp
<%--
  Created by IntelliJ IDEA.
  User: Weili
  Date: 2021/8/26
  Time: 21:29
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Upload</title>
</head>
<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username" /><br />
        头  像:<input type="file" name="photo"><br />
        <input type="submit" value="上传" />
    </form>
</body>
</html>
  • web.xml
<servlet>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>com.atjava.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/upload</url-pattern>
</servlet-mapping>

文件的下载介绍

文件下载的操作过程

  1. 客户端发送请求,通知服务器要下载的文件
  2. 服务器获取要下载的文件名
  3. 读取要下载的文件内容
  4. 将下载的文件内容回传给客户端
  5. 在回传前,通过 响应头通知客户端返回的数据类型
  6. 在回传前,要通知客户端收到的数据是用于下载使用(使用响应头)

下载的常用 API说明

response.getOutputStream(); // 获取响应的输出流

servletContext.getResourceAsStream(); // 以流的形式获取文件内容

servletContext.getMimeType(); // 获取要返回的文件类型

response.setContentType(); // 设置返回客户端的数据类型

实例:

  • DownloadServlet.java
package com.atjava.servlet;

import org.apache.commons.io.IOUtils;

import javax.servlet.ServletContext;
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.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

/**
 * @author lv
 * @create 2021-08-31 21:56
 */
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doGet(req, resp);
        // 1.获取要下载的文件名
        String downLoadFileName = "2.jpg";

        // 2.读取要下载的文件内容(通过 ServletContent对象读取)
        ServletContext servletContext = getServletContext();
        /**
         * "/"斜杠被服务器解析表示地址为 http://ip:port/工程名/ 映射到代码的 web目录
         */
        InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downLoadFileName);

        // 4.在回传前,通过响应头告诉客户端返回的数据类型
        // 获取要返回的文件类型:image/jpeg = 大类型/小类型
        String mimeType = servletContext.getMimeType("/file/" + downLoadFileName);
        resp.setContentType(mimeType);
        
        // 5.同时要告诉客户端收到的数据是用于下载的(设置响应头)
        // attachment 附件:表示下载使用
        // Content-Disposition 响应头属性:表示客户端收到的内容如何处理
        // filename 文件名:表示要下载的文件名,可以与源文件名不同,自定义下载的文件名
        // resp.setHeader("Content-Disposition", "attachment; filename=22.jpg");
        // URLEncoder.encode("中文.xxx", "UTF-8"):是将内容转换为 %xx%xx的格式;(URLEncoder编码:chrome、IE适用)
        /**
         * 文件名中出现中文,会出现乱码问题,http没有考虑中文情况,默认不支持中文
         * 解决:需要对中文进行编码,才能在网络中传输,
         * 对文件名进行 url编码:URLEncoder.encode("中文.xxx", "UTF-8");
         */
        resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downLoadFileName, "UTF-8"));

        /**
         * fireFox适用的编码:BASE64Encoder
         */
//        String fileName = "中文乱码";
//        BASE64Encoder base64Encoder = new BASE64Encoder();
//        String encodeStr = base64Encoder.encode(fileName.getBytes("UTF-8"));
//        System.out.println(encodeStr);

        // 3.将下载的文件内容回传给客户端
        // 获取响应的输出流
        OutputStream outputStream = resp.getOutputStream();
        // copy(): 读取 resourceAsStream中的数据,复制给 outputStream流,输出给客户端
        IOUtils.copy(resourceAsStream, outputStream);

    }
}
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>initParam</param-name>
        <param-value>Map</param-value>
    </context-param>
    <context-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.atjava.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>DownloadServlet</servlet-name>
        <servlet-class>com.atjava.servlet.DownloadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownloadServlet</servlet-name>
        <url-pattern>/download</url-pattern>
    </servlet-mapping>
</web-app>

中文乱码问题:

URLEncoder编码,适用于 Chrome、IE

// 5.同时要告诉客户端收到的数据是用于下载的(设置响应头)
// attachment 附件:表示下载使用
// Content-Disposition 响应头属性:表示客户端收到的内容如何处理
// filename 文件名:表示要下载的文件名,可以与源文件名不同,自定义下载的文件名
// resp.setHeader("Content-Disposition", "attachment; filename=22.jpg");
// URLEncoder.encode("中文.xxx", "UTF-8"):是将内容转换为 %xx%xx的格式;(URLEncoder编码:chrome、IE适用)
/**
 * 文件名中出现中文,会出现乱码问题,http没有考虑中文情况,默认不支持中文
 * 解决:需要对中文进行编码,才能在网络中传输,
 * 对文件名进行 url编码:URLEncoder.encode("中文.xxx", "UTF-8");
 */
resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downLoadFileName, "UTF-8"));

Base64编码、解码,适用于 fireFox

package com.atjava.base64Test;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * @author lv
 * @create 2021-08-31 23:00
 */
public class Base64Test {
    public static void main(String[] args) {
        /**
         * fireFox适用的编码操作:BASE64Encoder
         * 解码操作:
         */
        String fileName = "中文乱码";
        // 创建 Base64编码器
        BASE64Encoder base64Encoder = new BASE64Encoder();
        String encodeStr = null;
        try {
            // 执行编码操作:
            encodeStr = base64Encoder.encode(fileName.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        System.out.println(encodeStr); // 5Lit5paH5Lmx56CB

        // 创建 Base64解码器
        BASE64Decoder base64Decoder = new BASE64Decoder();
        byte[] decodeBuffer = null;
        try {
            // 解码操作:
            decodeBuffer = base64Decoder.decodeBuffer(encodeStr);
            fileName = new String(decodeBuffer, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(fileName); // 中文乱码
    }
}

BASE64 编解码,解决 火狐浏览器中附件中文名问题

如果客户端浏览器是火狐浏览器,那我们需要对中文名进行 BASE64的编码操作

  • Content-Disposition: attachment; filename==?charset?B?xxxx?=
  1. =? 表示编码内容的开始
  2. charset 表示字符集
  3. B 表示 BASE64编码
  4. xxxx 表示 BASE64编码后的中文内容
  5. ?= 表示编码内容的结束
resp.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" + new BASE64Encoder().encode("中文乱码.jpg".getBytes("UTF-8")) + "?=");