当前位置: 首页 > news >正文

EL表达式与JSTL标签库实战指南:从基础到OA系统改造

一、EL表达式

  • EL表达式是干什么用的?

    • Expression Language(表达式语言)

    • EL表达式可以代替JSP中的java代码,让JSP文件中的程序看起来更加整洁,美观。

    • JSP中夹杂着各种java代码,例如<% java代码 %>、<%=%>等,导致JSP文件很混乱,不好看,不好维护。所以才有了后期的EL表达式。

    • EL表达式可以算是JSP语法的一部分。EL表达式归属于JSP。

  • EL表达式出现在JSP中主要是:

    • 从某个作用域中取数据,然后将其转换成字符串,然后将其输出到浏览器。这就是EL表达式的功效。三大功效:

      • 第一功效:从某个域中取数据。

        • 四个域:

          • pageContext

          • request

          • session

          • application

      • 第二功效:将取出的数据转成字符串。

        • 如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串。

      • 第三功效:将字符串输出到浏览器。

        • 和这个一样:<%= %>,将其输出到浏览器。

  • EL表达式很好用,基本的语法格式:

    • ${表达式}

1.EL表达式的使用:

演示代码:

User类

package bean;

/**
 * 符合javabean规范的一个java类。
 */
public class User {
    private String username;
    private String password;
    private int age;
    private Address addr;

    public Address getAddr222() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    /*@Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }*/

    public User() {
    }

    public User(String username, String password, int age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }

    public String getUsername() {
        System.out.println("getUsername()方法执行了");
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        System.out.println("getPassword()方法执行了");
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    // 或者getage()
    // java程序员给方法起名的时候,建议驼峰。
    public int getAge() {
        System.out.println("getAge()方法执行了");
        return age;
    }

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

    /**
     * 这个方法只是一个get方法而已。类中并没有声明email属性。
     * 使用EL表达式可以获取Email吗?
     * @return
     */
    public String getEmail(){
        return "dujubin@126.com";
    }
}

 Address类

package bean;

public class Address {
    private String city;
    private String street;
    private String zipcode;

    public Address() {
    }

    public Address(String city, String street, String zipcode) {
        this.city = city;
        this.street = street;
        this.zipcode = zipcode;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }
}

7.jsp

<%@ page import="bean.User" %>
<%@ page import="bean.Address" %>
<%@page contentType="text/html;charset=UTF-8" %>

<%
    // 创建User对象
    User user = new User();
    user.setUsername("jackson");
    user.setPassword("1234");
    user.setAge(50);

    // 创建地址Address对象
    Address a = new Address();
    a.setCity("北京");
    a.setStreet("大兴区");
    a.setZipcode("11111111");

    user.setAddr(a);

    // 将User对象存储到request域当中
    request.setAttribute("userObj", user);
%>

<%--使用EL表达式,从request域当中,取出User对象,并将其输出到浏览器--%>
<%--1. EL表达式会自动从某个范围中取数据。2. 将其转成字符串。 3. 将其输出到浏览器。--%>
${userObj}

<br>
<%--你想输出的是user对象的username属性--%>
${userObj.username}
<br>
<%--输出password--%>
${userObj.password}
<br>
<%--输出年龄age--%>
${userObj.age}
<br>
<%--输出email--%>
${userObj.email}

<br>
<%--在EL表达式中不能添加双引号,如果添加了双引号,EL表达式就会将其当做普通的字符串输出到浏览器。--%>
${"userObj"}
userObj

<br>
<%--取出User对象是哪个城市的?--%>
城市:${userObj.addr222.city}
街道:${userObj.addr222.street}
邮编:${userObj.addr222.zipcode}

2.EL表达式优先从小范围中读取数据。

    • pageContext < request < session < application

  • EL表达式中有四个隐含的隐式的范围:

    • pageScope 对应的是 pageContext范围。

    • requestScope 对应的是 request范围。

    • sessionScope 对应的是 session范围。

    • applicationScope 对应的是 application范围。

<%@page contentType="text/html;charset=UTF-8" %>

<%
    // 四个域都存储了数据,并且name相同。
    session.setAttribute("data", "session");
    request.setAttribute("data", "request");
    pageContext.setAttribute("data", "pageContext");
    application.setAttribute("data", "application");
%>

<%--在没有指定范围的前提下,EL表达式优先从小范围中取数据--%>
<%--pageContext < request < session < application --%>
${data}

<hr>
<%-- 在EL表达式中可以指定范围来读取数据--%>
<%--EL表达式有4个隐含的隐式的范围对象--%>
<%--pageScope requestScope sessionScope applicationScope--%>
<%--以下是指定范围取数据。--%>
${pageScope.data}<br>
${requestScope.data}<br>
${sessionScope.data}<br>
${applicationScope.data}<br>

<%--在实际开发中,因为向某个域中存储数据的时候,name都是不同的。所以 xxxScope  都是可以省略的。--%>

3.EL表达式对null进行了预处理。如果是null,则向浏览器输出一个空字符串。

<%@page contentType="text/html;charset=UTF-8" %>

<%
    request.setAttribute("username", "zhangsan");
%>

<%--取出数据并且输出到浏览器--%>
<%=request.getAttribute("username")%><br>

采用EL表达式:${username}

<br>

<%=request.getAttribute("usernam")%><br>

<%--EL表达式主要任务是做页面展示,要求最终页面展示上是友好的。--%>
<%--所以EL表达式对null进行了处理。如果是null,则在浏览器上显示空白。--%>
采用EL表达式:${usernam}

<hr>
<%--EL表达式表面上是这种写法,实际上运行的时候,还是要翻译生成java代码的。--%>
${usernam} 这个EL表达式等同于这一段java代码:<%=request.getAttribute("usernam") == null ? "" : request.getAttribute("usernam")%>

4.EL表达式取数据的时候有两种形式:

  • 第一种:. (大部分使用这种方式)

  • 第二种:[ ] (如果存储到域的时候,这个name中含有特殊字符,可以使用 [ ])

    • request.setAttribute("abc.def", "zhangsan");

    • ${requestScope.abc.def} 这样是无法取值的。

    • 应该这样:${requestScope["abc.def"]}

<%@ page import="bean.User" %>
<%@page contentType="text/html;charset=UTF-8" %>

<%
    // 创建user对象
    User user = new User();
    user.setUsername("zhangsan");

    // 存储到request域当中。
    request.setAttribute("fdsafdsafdsa", user);

    // 向request域当中存储数据。
    request.setAttribute("abc.def", "hello jsp el!!!");
%>

<%--使用EL表达式取出,并且输出到浏览器--%>
<%--从域当中取user--%>
${fdsafdsafdsa}<br>

<%--取user的username--%>
<%----%>
${fdsafdsafdsa.username}<br>

<%--取user的username,注意[]当中的需要添加 双引号--%>
<%--[] 里面的没有加双引号的话,会将其看做变量。如果是带双引号 "username",则去找user对象的username属性。--%>
${fdsafdsafdsa["username"]}<br>

<%--将数据取出并输出到浏览器--%>
${requestScope.abc.def}<br>
之前是空白滴:<br>
${requestScope["abc.def"]}

5.掌握使用EL表达式,怎么从Map集合中取数据:

  • ${map.key}

  • 掌握使用EL表达式,怎么从数组和List集合中取数据:

    • ${数组[0]}

    • ${数组[1]}

    • ${list[0]}

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>EL表达式示例</title>
</head>
<body>
<%
  // 创建一个Map集合
  java.util.Map<String, String> map = new java.util.HashMap<>();
  map.put("key1", "Map中的值1");
  map.put("key2", "Map中的值2");
  request.setAttribute("map", map);

  // 创建一个数组
  String[] array = {"数组元素1", "数组元素2"};
  request.setAttribute("array", array);

  // 创建一个List集合
  java.util.List<String> list = new java.util.ArrayList<>();
  list.add("List元素1");
  list.add("List元素2");
  request.setAttribute("list", list);
%>

<h2>从Map集合中取数据</h2>
<p>key1对应的值: ${map.key1}</p>
<p>key2对应的值: ${map.key2}</p>

<h2>从数组中取数据</h2>
<p>数组第一个元素: ${array[0]}</p>
<p>数组第二个元素: ${array[1]}</p>

<h2>从List集合中取数据</h2>
<p>List第一个元素: ${list[0]}</p>
<p>List第二个元素: ${list[1]}</p>
</body>
</html>

6.page指令当中,有一个属性,可以忽略EL表达式

<%--<%@page contentType="text/html;charset=UTF-8" isELIgnored="false" %>--%>
<%--<%@page contentType="text/html;charset=UTF-8" %>--%>
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>

<%
    request.setAttribute("username", "zhangsan");
%>

<%-- isELIgnored="true" 表示忽略JSP中整个页面的所有EL表达式。如果想忽略其中某个,可以使用以下反斜杠。 --%>
\${username}
${username}
${username}

<%--<%@page contentType="text/html;charset=UTF-8" isELIgnored="false" %>--%>
<%@page contentType="text/html;charset=UTF-8" %>
<%--<%@page contentType="text/html;charset=UTF-8" isELIgnored="true" %>--%>

<%
    request.setAttribute("username", "zhangsan");
%>

<%-- isELIgnored="true" 表示忽略JSP中整个页面的所有EL表达式。如果想忽略其中某个,可以使用以下反斜杠。 --%>
\${username}
${username}
${username}

7.通过EL表达式获取应用的根:

1. JSP 内置对象

JSP 有九大内置对象,分别是 pageContextrequestsessionapplicationresponseoutconfigpageexception。其中 pageContextrequestsessionapplication 为四个域对象,pageContext 是作用域最小的域对象,代表页面上下文。

2. pageContext 的使用

  • pageContext.getRequest() 方法可以获取 request 对象,但在 JSP 中可以直接使用内置对象 request
  • pageContext 可用于获取其他内置对象和属性,是一个功能强大的上下文对象。

3. EL 表达式隐式对象

  • EL 表达式中没有 request 这个隐式对象,requestScope 仅代表 “请求范围”,不等同于 request 对象。
  • EL 表达式中有隐式对象 pageContext,它和 JSP 中的九大内置对象 pageContext 是同一个对象。

4. 获取应用根路径

  • 在 JSP 中可以通过 ((HttpServletRequest)pageContext.getRequest()).getContextPath() 获取应用的根路径。
  • 在 EL 表达式中可以使用 ${pageContext.request.contextPath} 来获取应用的根路径。
  • ${pageContext.request.contextPath}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="jakarta.servlet.http.HttpServletRequest" %>
<!DOCTYPE html>
<html>
<head>
  <title>JSP 和 EL 表达式获取应用根路径示例</title>
</head>
<body>
<h2>使用 JSP 代码获取应用根路径</h2>
<p>应用根路径 (JSP 代码): <%=((HttpServletRequest)pageContext.getRequest()).getContextPath() %></p>

<h2>使用 EL 表达式获取应用根路径</h2>
<p>应用根路径 (EL 表达式): ${pageContext.request.contextPath}</p>
</body>
</html>

  • 在 JSP 代码部分,通过 ((HttpServletRequest)pageContext.getRequest()).getContextPath() 获取应用的根路径并输出。
  • 在 EL 表达式部分,使用 ${pageContext.request.contextPath} 获取应用的根路径并输出。

8.EL表达式中其他的隐式对象:

⑴. EL 表达式隐含对象概述

EL(Expression Language)表达式提供了多个隐含对象,这些对象可以简化 JSP 页面中对数据的访问,主要有 pageContextparamparamValuesinitParam 等。

⑵. 各隐含对象的作用

pageContext
  • 它代表 JSP 的页面上下文,可用于获取其他 JSP 内置对象和属性。例如,通过 pageContext.request.contextPath 可以获取应用的根路径。
param
  • 用于获取请求参数的单个值。当请求中存在同名参数时,param 只会获取该参数一维数组中的第一个元素。

param 获取的参数来源

param隐式对象用于获取请求参数,具体包括:

URL 中的查询参数(GET 请求):例如 http://localhost:8080/page.jsp?username=lisi 中的 username=lisi。

表单提交的参数(POST 请求):例如表单中 <input type="text" name="password"> 的值。

HTTP 请求头中的参数(较少见,需特殊处理)。

 应用场景

param常用于以下场景:

用户登录:获取用户名和密码。

搜索功能:获取用户输入的关键词。

分页参数:获取当前页码或每页显示数量。

动态页面参数:通过 URL 传递 ID(如 http://localhost:8080/article.jsp?id=123)。

<%@page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
<body>
用户名(JSP):<%=request.getParameter("username")%><br>
用户名(EL):${param.username}
</body>
</html>
paramValues
  • 用于获取请求参数的所有值,返回一个数组。可以通过索引访问数组中的具体元素。
<%@page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
<body>
<%--假设用户提交的数据:http://localhost:8080/jsp/test.jsp?aihao=smoke&aihao=drink&aihao=tangtou--%>
<%--以上提交的数据显然是采用checkbox进行提交的。同一组的checkbox的name是一样的。--%>
<%--param 获取的是请求参数一维数组当中的第一个元素。--%>
爱好:${param.aihao} <br>
爱好:<%=request.getParameter("aihao")%> <br>

一维数组:${paramValues.aihao}<br>
一维数组:<%=request.getParameterValues("aihao")%><br>

<%--获取数组当中的元素:[下标]--%>
爱好:${paramValues.aihao[0]}、${paramValues.aihao[1]}、${paramValues.aihao[2]} <br>
</body>
</html>

initParam
  • 用于获取在 web.xml 中配置的应用初始化参数。这些参数是应用级别的,可通过 ServletContext(在 JSP 中对应的是 application 内置对象)获取。
<%@page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
<body>
每页显示记录数(JSP):<%=application.getInitParameter("pageSize")%><br>
每页显示记录数(EL):${initParam.pageSize}<br>
页码(JSP):<%=application.getInitParameter("pageNum")%><br>
页码(EL):${initParam.pageNum}
<br>
<br>
<br>
<%--EL表达式中的隐含对象:initParam--%>
<%--ServletContext是Servlet上下文对象,对应的JSP九大内置对象之一是:application --%>
<%
    String a = application.getInitParameter("pageSize");
    String b = application.getInitParameter("pageNum");
%>

每页显示的记录条数:<%=a%> <br>
页码:<%=b%> <br>

每页显示的记录条数:<%=application.getInitParameter("pageSize")%> <br>
页码:<%=application.getInitParameter("pageNum")%> <br>


每页显示的记录条数:${initParam.pageSize} <br>
页码:${initParam.pageNum} <br>
</body>
</html>

⑶. 与 JSP 内置对象的对比

  • JSP 有九大内置对象,如 requestapplication 等。在获取请求参数和应用初始化参数时,既可以使用 JSP 内置对象的方法,也可以使用 EL 表达式的隐含对象,EL 表达式的使用更加简洁。

9.EL表达式的运算符

⑴. 算术运算符

在 EL 表达式中,算术运算符有 +-*/%其中 + 只能进行求和运算,不能用于字符串拼接。当 + 两边的数据不是数字时,会尝试将其转换为数字,如果转换失败则会抛出 NumberFormatException

⑵. 关系运算符

关系运算符包括 ==!=>>=<<=,也可以使用 eq 替代 ==。在 EL 表达式中,== 和 != 会调用对象的 equals 方法来比较对象的值是否相等,而不是比较对象的引用。

⑶. 逻辑运算符

逻辑运算符有 !(取反)、&&(逻辑与)、||(逻辑或),也可以使用 not 替代 !and 替代 &&or 替代 ||。使用时需要注意逻辑优先级,必要时使用括号来明确运算顺序。

⑷ 条件运算符

条件运算符为 ? :,其语法为 条件表达式 ? 表达式1 : 表达式2。如果条件表达式为 true,则返回表达式 1 的值;否则返回表达式 2 的值。

⑸ 取值运算符

取值运算符有 [] 和 .[] 通常用于访问数组、列表或映射中的元素,用于访问对象的属性或方法。

⑹ empty 运算符

empty 运算符用于判断一个值是否为空,运算结果是 boolean 类型。如果值为空,则结果为 true;否则为 false。可以使用 ! 或 not 对 empty 运算符的结果取反。

二、JSTL标签库

1.什么是JSTL标签库?

  • Java Standard Tag Lib(Java标准的标签库)

  • JSTL标签库通常结合EL表达式一起使用。目的是让JSP中的java代码消失。

  • 标签是写在JSP当中的,但实际上最终还是要执行对应的java程序。(java程序在jar包当中。)

2.使用JSTL标签库的步骤:

  • 第一步:引入JSTL标签库对应的jar包。

    • tomcat10之后引入的jar包是:

      • jakarta.servlet.jsp.jstl-2.0.0.jar

      • jakarta.servlet.jsp.jstl-api-2.0.0.jar

    • 在IDEA当中怎么引入?

      • 在WEB-INF下新建lib目录,然后将jar包拷贝到lib当中。然后将其“Add Lib...”

      • 一定是要和mysql的数据库驱动一样,都是放在WEB-INF/lib目录下的。

      • 什么时候需要将jar包放到WEB-INF/lib目录下?如果这个jar是tomcat服务器没有的。

maven就是将需要的工件放到WEB-INF/lib目录下

  • 第二步:在JSP中引入要使用标签库。(使用taglib指令引入标签库。)

    • JSTL提供了很多种标签,你要引入哪个标签????重点掌握核心标签库。

第三步:在需要使用标签的位置使用即可。表面使用的是标签,底层实际上还是java程序。

3.JSTL标签的原理

源码解析:配置文件tld解析

演示代码:

 Student类

package bean;

import java.util.Objects;

public class Student {
    private String id;
    private String name;

    @Override
    public boolean equals(Object o) {
        System.out.println("equals方法执行了");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    public Student() {
    }

    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

17.jsp

<%@ page import="bean.Student" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@page contentType="text/html;charset=UTF-8" %>

<%--引入标签库。这里引入的是jstl的核心标签库。--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%--格式化标签库,专门负责格式化操作的。--%>
<%--<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>--%>

<%--sql标签库--%>
<%--<%@taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>--%>

<%
    // 创建List集合
    List<Student> stuList = new ArrayList<>();

    // 创建Student对象
    Student s1 = new Student("110", "经常");
    Student s2 = new Student("120", "救护车");
    Student s3 = new Student("119", "消防车");

    // 添加到List集合中
    stuList.add(s1);
    stuList.add(s2);
    stuList.add(s3);

    // 将list集合存储到request域当中
    request.setAttribute("stuList", stuList);
%>

<%--需求:将List集合中的元素遍历。输出学生信息到浏览器--%>
<%--使用java代码--%>
<%
    // 从域中获取List集合
    List<Student> stus = (List<Student>)request.getAttribute("stuList");
    // 编写for循环遍历list集合
    for(Student stu : stus){
%>
    id:<%=stu.getId()%>,name:<%=stu.getName()%><br>
<%
    }
%>

<hr>

<%--使用core标签库中forEach标签。对List集合进行遍历--%>
<%--EL表达式只能从域中取数据。--%>
<%--var后面的名字是随意的。var属性代表的是集合中的每一个元素。--%>
<c:forEach items="${stuList}" var="s">
    id:${s.id},name:${s.name} <br>
</c:forEach>


4.jstl中的核心标签库core当中有哪些常用的标签呢?

  • c:if

    • <c:if test="boolean类型,支持EL表达式"></c: if>

  • c:forEach

    • <c:forEach items="集合,支持EL表达式" var="集合中的元素" varStatus="元素状态对象"> ${元素状态对象.count} </c: forEach>

    • <c:forEach var="i" begin="1" end="10" step="2"> ${i} </c: forEach>

  • c:choose c:when c:otherwise

⑴.<c:if> 标签

  • 功能<c:if> 标签用于根据条件判断是否执行标签体内容,类似于 Java 中的 if 语句。
  • 属性
    • test:必填属性,其值为布尔类型,可以使用 EL 表达式。当 test 的值为 true 时,执行标签体内容;为 false 时,不执行。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>c:if 标签测试</title>
</head>
<body>
    <%
        int num = 10;
        request.setAttribute("number", num);
    %>
    <c:if test="${number > 5}">
        <p>数字 ${number} 大于 5。</p>
    </c:if>
</body>
</html>

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

  • 功能:这三个标签组合使用,类似于 Java 中的 if - else if - else 语句,用于进行多条件判断。
  • 使用方式
    • <c:choose> 作为父标签,包裹 <c:when> 和 <c:otherwise> 标签。
    • <c:when> 标签可以有多个,每个 <c:when> 标签都有一个 test 属性,用于指定条件。当某个 <c:when> 的 test 条件为 true 时,执行该标签体内容,并且后续的 <c:when> 和 <c:otherwise> 标签不再执行。
    • <c:otherwise> 标签是可选的,当所有 <c:when> 的条件都为 false 时,执行 <c:otherwise> 标签体内容。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>c:choose、c:when 和 c:otherwise 标签测试</title>
</head>
<body>
<%
    int score = 85;
    request.setAttribute("score", score);
%>
<c:choose>
    <c:when test="${score >= 90}">
        <p>成绩优秀!</p>
    </c:when>
    <c:when test="${score >= 80}">
        <p>成绩良好!</p>
    </c:when>
    <c:when test="${score >= 60}">
        <p>成绩及格!</p>
    </c:when>
    <c:otherwise>
        <p>成绩不及格!</p>
    </c:otherwise>
</c:choose>
</body>
</html>

三、改造OA

  • 使用什么技术改造呢?

    • Servlet + JSP + EL表达式 + JSTL标签。进行改造。

  • 在前端HTML代码中,有一个标签,叫做base标签,这个标签可以设置整个网页的基础路径。

    • 这是Java的语法,也不是JSP的语法。是HTML中的一个语法。HTML中的一个标签。通常出现在head标签中。

    • < base href="http://localhost:8080/oa/">

    • 在当前页面中,凡是路径没有以“/”开始的,都会自动将base中的路径添加到这些路径之前。

      • < a href="ab/def"></ a>

      • 等同于:< a href="http://localhost:8080/oa/ab/def"></ a>

    • 需要注意:在JS代码中的路径,保险起见,最好不要依赖base标签。JS代码中的路径最好写上全路径。

四、Filter过滤器

  • 当前的OA项目存在什么缺陷?

    • DeptServlet、EmpServlet、OrderServlet。每一个Servlet都是处理自己相关的业务。在这些Servlet执行之前都是需要判断用户是否登录了。如果用户登录了,可以继续操作,如果没有登录,需要用户登录。这段判断用户是否登录的代码是固定的,并且在每一个Servlet类当中都需要编写,显然代码没有得到重复利用。包括每一个Servlet都要解决中文乱码问题,也有公共的代码。这些代码目前都是重复编写,并没有达到复用。怎么解决这个问题?

      • 可以使用Servlet规范中的Filter过滤器来解决这个问题。

  • Filter是什么,有什么用,执行原理是什么?

    • Filter是过滤器。

    • Filter可以在Servlet这个目标程序执行之前添加代码。也可以在目标Servlet执行之后添加代码。之前之后都可以添加过滤规则。

    • 一般情况下,都是在过滤器当中编写公共代码。

1.一个过滤器怎么写呢?

  • 第一步:编写一个Java类实现一个接口:jarkata.servlet.Filter。并且实现这个接口当中所有的方法。

    • init方法:在Filter对象第一次被创建之后调用,并且只调用一次。

    • doFilter方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。

    • destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。

  • 第二步:在web.xml文件中对Filter进行配置。这个配置和Servlet很像。

      • 或者使用注解:@WebFilter({"*.do"})

  • 注意:

    • Servlet对象默认情况下,在服务器启动的时候是不会新建对象的。

    • Filter对象默认情况下,在服务器启动的时候会新建对象。

    • Servlet是单例的。Filter也是单例的。(单实例。)

  • 目标Servlet是否执行,取决于两个条件:

    • 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。

    • 第二:用户发送的请求路径是否和Servlet的请求路径一致。

  • chain.doFilter(request, response); 这行代码的作用:

    • 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。

  • 注意:Filter的优先级,天生的就比Servlet优先级高。

    • /a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。

  • 关于Filter的配置路径:

    • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。

    • /* 匹配所有路径。

    • *.do 后缀匹配。不要以 / 开始

    • /dept/* 前缀匹配。

  • 在web.xml文件中进行配置的时候,Filter的执行顺序是什么?

    • 依靠filter-mapping标签的配置位置,越靠上优先级越高。

  • 过滤器的调用顺序,遵循栈数据结构。

  • 使用@WebFilter的时候,Filter的执行顺序是怎样的呢?

    • 执行顺序是:比较Filter这个类名。

    • 比如:FilterA和FilterB,则先执行FilterA。

    • 比如:Filter1和Filter2,则先执行Filter1.

  • Filter的生命周期?

    • 和Servlet对象生命周期一致。

    • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。

  • Filter过滤器这里有一个设计模式:

    • 责任链设计模式。

    • 过滤器最大的优点:

      • 在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。

    • 责任链设计模式最大的核心思想:

      • 在程序运行阶段,动态的组合程序的调用顺序。

  • 使用过滤器改造OA项目。

五、Listener监听器

1.什么是监听器?

  • 监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。

  • 在Servlet中,所有的监听器接口都是以“Listener”结尾。

2.监听器有什么用?

  • 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。

  • 特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。

3.Servlet规范中提供了哪些监听器?

  • HttpSessionListener

  • HttpSessionAttributeListener

    • 该监听器需要使用@WebListener注解进行标注。

    • 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。

  • HttpSessionBindingListener

    • 该监听器不需要使用@WebListener进行标注。

    • 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。

    • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。

  • HttpSessionIdListener

    • session的id发生改变的时候,监听器中的唯一一个方法就会被调用。

  • HttpSessionActivationListener

    • 监听session对象的钝化和活化的。

    • 钝化:session对象从内存存储到硬盘文件。

    • 活化:从硬盘文件把session恢复到内存。

  • 实现一个监听器的步骤:以ServletContextListener为例。

    • 第一步:编写一个类实现ServletContextListener接口。并且实现里面的方法。

      • void contextInitialized(ServletContextEvent event)
        void contextDestroyed(ServletContextEvent event)
    • 第二步:在web.xml文件中对ServletContextListener进行配置,如下:

      • <listener>
            <listener-class>com.bjpowernode.javaweb.listener.MyServletContextListener</listener-class>
        </listener>
      • 当然,第二步也可以不使用配置文件,也可以用注解,例如:@WebListener

  • 注意:所有监听器中的方法都是不需要javaweb程序员调用的,由服务器来负责调用?什么时候被调用呢?

    • 当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。

  • 思考一个业务场景:

    • 请编写一个功能,记录该网站实时的在线用户的个数。

    • 我们可以通过服务器端有没有分配session对象,因为一个session代表了一个用户。有一个session就代表有一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了。session对象只要新建,则count++,然后将count存储到ServletContext域当中,在页面展示在线人数即可。

    • 业务发生改变了,只统计登录的用户的在线数量,这个该怎么办?

      • session.setAttribute("user", userObj);

      • 用户登录的标志是什么?session中曾经存储过User类型的对象。那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要User类型对象存储到session域中,则count++,然后将count++存储到ServletContext对象中。页面展示在线人数即可。

  • 实现oa项目中当前登录在线的人数。

    • 什么代表着用户登录了?

      • session.setAttribute("user", userObj); User类型的对象只要往session中存储过,表示有新用户登录。

    • 什么代表着用户退出了?

      • session.removeAttribute("user"); User类型的对象从session域中移除了。

      • 或者有可能是session销毁了。(session超时)

六、oa案例

1.Dept类

package oa.bean;

import java.util.Objects;

/**
 * 一个普通的java类,这个java类可以封装零散的数据。代表了一个部门对象。
 */
public class Dept {
    private String deptno;
    private String dname;
    private String loc;

    public Dept() {
    }

    public String getDeptno() {
        return deptno;
    }

    public void setDeptno(String deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno='" + deptno + '\'' +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dept dept = (Dept) o;
        return Objects.equals(deptno, dept.deptno) && Objects.equals(dname, dept.dname) && Objects.equals(loc, dept.loc);
    }

    @Override
    public int hashCode() {
        return Objects.hash(deptno, dname, loc);
    }
}

2.User

package oa.bean;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;

public class User implements HttpSessionBindingListener {

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        // 用户登录了
        // User类型的对象向session中存放了。
        // 获取ServletContext对象
        ServletContext application = event.getSession().getServletContext();
        // 获取在线人数。
        Object onlinecount = application.getAttribute("onlinecount");
        if (onlinecount == null) {
            application.setAttribute("onlinecount", 1);
        } else {
            int count = (Integer)onlinecount;
            count++;
            application.setAttribute("onlinecount", count);
        }

    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        // 用户退出了
        // User类型的对象从session域中删除了。
        ServletContext application = event.getSession().getServletContext();
        Integer onlinecount = (Integer)application.getAttribute("onlinecount");
        onlinecount--;
        application.setAttribute("onlinecount", onlinecount);
    }

    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

3.DBUtil

package oa.utils;

import java.sql.*;
import java.util.ResourceBundle;

/**
 * JDBC的工具类
 */
public class DBUtil {

    // 静态变量:在类加载时执行。
    // 并且是有顺序的。自上而下的顺序。
    // 属性资源文件绑定
    private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc");
    // 根据属性配置文件key获取value
    private static String driver = bundle.getString("driver");
    private static String url = bundle.getString("url");
    private static String user = bundle.getString("user");
    private static String password = bundle.getString("password");

    static {
        // 注册驱动(注册驱动只需要注册一次,放在静态代码块当中。DBUtil类加载的时候执行。)
        try {
            // "com.mysql.jdbc.Driver" 是连接数据库的驱动,不能写死。因为以后可能还会连接Oracle数据库。
            // 如果连接oracle数据库的时候,还需要修改java代码,显然违背了OCP开闭原则。
            // OCP开闭原则:对扩展开放,对修改关闭。(什么是符合OCP呢?在进行功能扩展的时候,不需要修改java源代码。)
            //Class.forName("com.mysql.jdbc.Driver");

            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     * @return conn 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        // 获取连接
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }

    /**
     * 释放资源
     * @param conn 连接对象
     * @param ps 数据库操作对象
     * @param rs 结果集对象
     */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

4.DeptServlet

package oa.web.action;


import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import oa.bean.Dept;
import oa.utils.DBUtil;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"})
public class DeptServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        /*// post请求乱码问题
        request.setCharacterEncoding("UTF-8");
        // 响应中文乱码问题
        response.setContentType("text/html;charset=UTF-8");*/

        // 获取session(这个session是不需要新建的)
        // 只是获取当前session,获取不到这返回null
        /*HttpSession session = request.getSession(false);
        if(session != null && session.getAttribute("username") != null){
            String servletPath = request.getServletPath();
            if("/dept/list".equals(servletPath)){
                doList(request, response);
            }else if("/dept/detail".equals(servletPath)){
                doDetail(request, response);
            }else if("/dept/delete".equals(servletPath)){
                doDel(request, response);
            }else if("/dept/save".equals(servletPath)){
                doSave(request, response);
            }else if("/dept/modify".equals(servletPath)){
                doModify(request, response);
            }
        }else{
            // 跳转到登录页面
            //response.sendRedirect("/oa/index.jsp");
            //response.sendRedirect("/oa");
            response.sendRedirect(request.getContextPath() + "/index.jsp"); // 访问web站点的根即可,自动找到欢迎页面。
        }*/


        String servletPath = request.getServletPath();
        if("/dept/list".equals(servletPath)){
            doList(request, response);
        }else if("/dept/detail".equals(servletPath)){
            doDetail(request, response);
        }else if("/dept/delete".equals(servletPath)){
            doDel(request, response);
        }else if("/dept/save".equals(servletPath)){
            doSave(request, response);
        }else if("/dept/modify".equals(servletPath)){
            doModify(request, response);
        }

    }

    /**
     * 保存部门信息
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doSave(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取部门的信息
        // 注意乱码问题(Tomcat10不会出现这个问题)
        request.setCharacterEncoding("UTF-8");
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");

        // 连接数据库执行insert语句
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "insert into dept(deptno, dname, loc) values(?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, deptno);
            ps.setString(2, dname);
            ps.setString(3, loc);
            count = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, ps, null);
        }

        if (count == 1) {
            response.sendRedirect(request.getContextPath() + "/dept/list");
        }
    }

    /**
     * 根据部门编号删除部门
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doDel(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取部门编号
        String deptno = request.getParameter("deptno");
        // 连接数据库,删除部门
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, deptno);
            count = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, ps, null);
        }

        if (count == 1) {
            // 删除成功
            // 重定向到列表页面
            String contextPath = request.getContextPath();
            response.sendRedirect(contextPath + "/dept/list");
        }
    }

    /**
     * 根据部门编号获取部门的信息。
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doDetail(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 创建部门对象
        Dept dept = new Dept();

        // 获取部门编号
        String dno = request.getParameter("dno");

        // 根据部门编号获取部门信息,将部门信息封装成咖啡豆
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "select dname, loc from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, dno);
            rs = ps.executeQuery();
            // 这个结果集当中只有一条数据,不需要while循环
            if (rs.next()) {
                String dname = rs.getString("dname");
                String loc = rs.getString("loc");
                // 封装对象(创建豆子)
                dept.setDeptno(dno);
                dept.setDname(dname);
                dept.setLoc(loc);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, ps, rs);
        }

        // 这个豆子只有一个,所以不需要袋子,只需要将这个咖啡豆放到request域当中即可。
        request.setAttribute("dept", dept);

        // 转发(不是重定向,因为要跳转到JSP做数据展示)
        //request.getRequestDispatcher("/detail.jsp").forward(request, response);

        /*String f = request.getParameter("f");
        if ("m".equals(f)) {
            // 转发到修改页面
            request.getRequestDispatcher("/edit.jsp").forward(request, response);
        } else if("d".equals(f)){
            // 转发到详情页面
            request.getRequestDispatcher("/detail.jsp").forward(request, response);
        }*/

        request.getRequestDispatcher("/" + request.getParameter("f") + ".jsp").forward(request, response);

    }

    /**
     * 连接数据库,查询所有的部门信息,将部门信息收集好,然后跳转到JSP做页面展示。
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doList(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 准备一个容器,用来专门存储部门
        List<Dept> depts = new ArrayList<>();

        // 连接数据库,查询所有的部门信息
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 获取连接
            conn = DBUtil.getConnection();
            // 执行查询语句
            String sql = "select deptno,dname,loc from dept";
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            // 遍历结果集
            while (rs.next()) {
                // 从结果集中取出。
                String deptno = rs.getString("deptno");
                String dname = rs.getString("dname");
                String loc = rs.getString("loc");

                // 将以上的零散的数据封装成java对象。
                Dept dept = new Dept();
                dept.setDeptno(deptno);
                dept.setDname(dname);
                dept.setLoc(loc);

                // 将部门对象放到list集合当中
                depts.add(dept);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            DBUtil.close(conn, ps, rs);
        }

        // 将一个集合放到请求域当中
        request.setAttribute("deptList", depts);

        // 转发(不要重定向)
        request.getRequestDispatcher("/list.jsp").forward(request, response);

    }

    /**
     * 修改部门
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doModify(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 解决请求体的中文乱码问题。
        request.setCharacterEncoding("UTF-8");

        // 获取表单中的数据
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");
        // 连接数据库执行更新语句
        Connection conn = null;
        PreparedStatement ps = null;
        int count = 0;
        try {
            conn = DBUtil.getConnection();
            String sql = "update dept set dname = ?, loc = ? where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, dname);
            ps.setString(2, loc);
            ps.setString(3, deptno);
            count = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, ps, null);
        }

        if (count == 1) {
            response.sendRedirect(request.getContextPath() + "/dept/list");
        }
    }
}





5.EmpServlet

package oa.web.action;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

/**
 * 员工管理的。
 * 员工管理的前提也是需要先登录。
 */
public class EmpServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        /*// post请求乱码问题
        request.setCharacterEncoding("UTF-8");
        // 响应中文乱码问题
        response.setContentType("text/html;charset=UTF-8");*/

        HttpSession session = request.getSession(false);
        if(session != null && session.getAttribute("username") != null){
            String servletPath = request.getServletPath();
            //...
        }else{
            response.sendRedirect(request.getContextPath() + "/index.jsp");
        }
    }
}

6.OrderServlet

package oa.web.action;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

/**
 * 处理订单相关业务的类。
 * 订单处理的前提还是需要先登录,才能处理订单。
 */
public class OrderServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        /*// post请求乱码问题
        request.setCharacterEncoding("UTF-8");
        // 响应中文乱码问题
        response.setContentType("text/html;charset=UTF-8");*/

        HttpSession session = request.getSession(false);
        if(session != null && session.getAttribute("username") != null){
            String servletPath = request.getServletPath();
            //...
        }else{
            response.sendRedirect(request.getContextPath() + "/index.jsp");
        }
    }
}

7.UserServlet

package oa.web.action;


import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import oa.bean.User;
import oa.utils.DBUtil;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

// Servlet负责业务的处理
// JSP负责页面的展示。
@WebServlet({"/user/login","/user/exit"})
public class UserServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String servletPath = request.getServletPath();
        if("/user/login".equals(servletPath)){
            doLogin(request, response);
        }else if("/user/exit".equals(servletPath)){
            doExit(request, response);
        }
    }

    protected void doExit(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取session对象,销毁session
        HttpSession session = request.getSession(false);
        if (session != null) {

            // 从session域中删除user对象
            session.removeAttribute("user");

            // 手动销毁session对象。
            session.invalidate();

            // 销毁cookie(退出系统将所有的cookie全部销毁)
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                for (Cookie cookie : cookies) {
                    // 设置cookie的有效期为0,表示删除该cookie
                    cookie.setMaxAge(0);
                    // 设置一个下cookie的路径
                    cookie.setPath(request.getContextPath()); // 删除cookie的时候注意路径问题。
                    // 响应cookie给浏览器,浏览器端会将之前的cookie覆盖。
                    response.addCookie(cookie);
                }
            }

            // 换一种方案
            /*Cookie cookie1 = new Cookie("username","");
            cookie1.setMaxAge(0);
            cookie1.setPath(request.getContextPath());

            Cookie cookie2 = new Cookie("password", "");
            cookie2.setMaxAge(0);
            cookie2.setPath(request.getContextPath());

            response.addCookie(cookie1);
            response.addCookie(cookie2);*/


            // 跳转到登录页面
            response.sendRedirect(request.getContextPath());
        }
    }

    protected void doLogin(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        boolean success = false;
        // 你要做一件什么事儿?验证用户名和密码是否正确。
        // 获取用户名和密码
        // 前端你是这样提交的:username=admin&password=123
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 连接数据库验证用户名和密码
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "select * from t_user where username = ? and password = ?";
            // 编译SQL
            ps = conn.prepareStatement(sql);
            // 给?传值
            ps.setString(1, username);
            ps.setString(2, password);
            // 执行SQL
            rs = ps.executeQuery();
            // 这个结果集当中最多只有一条数据。
            if (rs.next()) { // 不需要while循环
                // 登录成功
                success = true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtil.close(conn, ps, rs);
        }

        // 登录成功/失败
        if (success) {
            // 获取session对象(这里的要求是:必须获取到session,没有session也要新建一个session对象。)
            HttpSession session = request.getSession(); // session对象一定不是null
            //session.setAttribute("username", username);

            User user = new User(username, password);
            session.setAttribute("user", user);

            // 登录成功了,并且用户确实选择了“十天内免登录”功能。
            String f = request.getParameter("f");
            if("1".equals(f)){
                // 创建Cookie对象存储登录名
                Cookie cookie1 = new Cookie("username", username);
                // 创建Cookie对象存储密码
                Cookie cookie2 = new Cookie("password", password); // 真实情况下是加密的。
                // 设置cookie的有效期为十天
                cookie1.setMaxAge(60 * 60 * 24 * 10);
                cookie2.setMaxAge(60 * 60 * 24 * 10);
                // 设置cookie的path(只要访问这个应用,浏览器就一定要携带这两个cookie)
                cookie1.setPath(request.getContextPath());
                cookie2.setPath(request.getContextPath());
                // 响应cookie给浏览器
                response.addCookie(cookie1);
                response.addCookie(cookie2);
            }

            // 成功,跳转到用户列表页面
            response.sendRedirect(request.getContextPath() + "/dept/list");
        } else {
            // 失败,跳转到失败页面
            response.sendRedirect(request.getContextPath() + "/error.jsp");
        }

    }
}



8.WelcomeServlet

package oa.web.action;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;
import oa.bean.User;
import oa.utils.DBUtil;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获取cookie
        // 这个Cookie[]数组可能是null,如果不是null,数组的长度一定是大于0的。
        Cookie[] cookies = request.getCookies();
        String username = null;
        String password = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                if("username".equals(name)){
                    username = cookie.getValue();
                }else if("password".equals(name)){
                    password = cookie.getValue();
                }
            }
        }

        // 要在这里使用username和password变量
        if(username != null && password != null){
            // 验证用户名和密码是否正确
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            boolean success = false;
            try {
                conn = DBUtil.getConnection();
                String sql = "select * from t_user where username = ? and password = ?";
                ps = conn.prepareStatement(sql);
                ps.setString(1,username);
                ps.setString(2,password);
                rs = ps.executeQuery();
                if (rs.next()) {
                    // 登录成功
                    success = true;
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                DBUtil.close(conn, ps, rs);
            }

            if (success) {
                // 获取session
                HttpSession session = request.getSession();
                //session.setAttribute("username", username);

                User user = new User(username, password);
                session.setAttribute("user", user);

                // 正确,表示登录成功
                response.sendRedirect(request.getContextPath() + "/dept/list");
            }else{
                // 错误,表示登录失败
                response.sendRedirect(request.getContextPath() + "/index.jsp");
            }
        }else{
            // 跳转到登录页面
            response.sendRedirect(request.getContextPath() + "/index.jsp");
        }

    }
}

 9.LoginCheckFilter

package oa.web.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import java.io.IOException;

public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        /**
         * 什么情况下不能拦截?
         *      目前写的路径是:/* 表示所有的请求均拦截。
         *
         *      用户访问 index.jsp的时候不能拦截
         *      用户已经登录了,这个需要放行,不能拦截。
         *      用户要去登录,这个也不能拦截。
         *      WelcomeServlet也不能拦截。
         */

        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse) resp;

        // 获取请求路径
        String servletPath = request.getServletPath();

        HttpSession session = request.getSession(false);
        /*if("/index.jsp".equals(servletPath) || "/welcome".equals(servletPath) ||
                "/user/login".equals(servletPath) || "/user/exit".equals(servletPath)
                || (session != null && session.getAttribute("username") != null)){*/

        if("/index.jsp".equals(servletPath) || "/welcome".equals(servletPath) ||
                "/user/login".equals(servletPath) || "/user/exit".equals(servletPath)
                || (session != null && session.getAttribute("user") != null)){

            // 继续往下走
            chain.doFilter(request, response);
        }else{
            response.sendRedirect(request.getContextPath() + "/index.jsp");
        }
    }
}

10.add.jsp

<%@page contentType="text/html;charset=UTF-8"%>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>新增部门</title>
	</head>
	<body>
	<h3>欢迎${username},在线人数${onlinecount}人</h3>
		<h1>新增部门</h1>
		<hr >
		<form action="${pageContext.request.contextPath}/dept/save" method="post">
			部门编号<input type="text" name="deptno"/><br>
			部门名称<input type="text" name="dname"/><br>
			部门位置<input type="text" name="loc"/><br>
			<input type="submit" value="保存"/><br>
		</form>
	</body>
</html>

11.detail.jsp

<%@page contentType="text/html;charset=UTF-8"%>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>部门详情</title>
	</head>
	<body>
	<h3>欢迎${username},在线人数${onlinecount}人</h3>
		<h1>部门详情</h1>
		<hr >
		部门编号:${dept.deptno} <br>
		部门名称:${dept.dname}<br>
		部门位置:${dept.loc}<br>
		
		<input type="button" value="后退" onclick="window.history.back()"/>
	</body>
</html>

12.edit.jsp

<%@page contentType="text/html;charset=UTF-8"%>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>修改部门</title>
	</head>
	<body>
	<h3>欢迎${username},在线人数${onlinecount}人</h3>
		<h1>修改部门</h1>
		<hr >
		<form action="${pageContext.request.contextPath}/dept/modify" method="post">
			部门编号<input type="text" name="deptno" value="${dept.deptno}" readonly /><br>
			部门名称<input type="text" name="dname" value="${dept.dname}"/><br>
			部门位置<input type="text" name="loc" value="${dept.loc}"/><br>
			<input type="submit" value="修改"/><br>
		</form>
	</body>
</html>

13.error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录失败</title>
</head>
<body>
登录失败,请<a href="${pageContext.request.contextPath}/index.jsp">重新登录</a>
</body>
</html>

14.index.jsp

<%@page contentType="text/html;charset=UTF-8"%>
<%--访问jsp的时候不生成session对象。--%>
<%@page session="false" %>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>欢迎使用OA系统</title>
	</head>
	<body>
		<%-- 前端发送请求路径的时候,如果请求路径是绝对路径,要以 / 开始,加项目名。--%>
		<%-- 以下这样写代码,oa项目名写死了。这种设计显然是不好的。--%>
		<%--<a href="/oa/list.jsp">查看部门列表</a>--%>

		<%--注意空格的问题。--%>
		<%--<a href="<%=request.getContextPath() %>/list.jsp">查看部门列表</a>--%>

		<%-- 执行一个Servlet,查询数据库,收集数据。--%>
		<%--<a href="<%=request.getContextPath() %>/dept/list">查看部门列表</a>--%>

		<%--<hr>--%>
		<%--调用哪个对象的哪个方法,可以动态的获取一个应用的根路径。--%>
		<%--<%=request.getContextPath() %>--%>   <%--   out.print(request.getContextPath());   --%>

        <h1>LOGIN PAGE</h1>
        <hr>
        <%--前端页面发送请求的时候,请求路径以“/”开始,带项目名。--%>
        <form action="${pageContext.request.contextPath}/user/login" method="post">
            username: <input type="text" name="username" ><br>
            password: <input type="password" name="password"><br>
            <input type="checkbox" name="f" value="1">十天内免登录<br>
            <input type="submit" value="login">
        </form>
	</body>
</html>

15.list.jsp

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html;charset=UTF-8"%>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>部门列表页面</title>
		<%--设置整个网页的基础路径是:http://localhost:8080/oa/ --%>
		<%--<base href="http://localhost:8080/oa/">--%>
		<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
	</head>
	<body>

	<h3>欢迎${username},在线人数${onlinecount}人</h3>
	<a href="user/exit">[退出系统]</a>


<script type="text/javascript">
	function del(dno){
		var ok = window.confirm("亲,删了不可恢复哦!");
		if(ok){
			/*注意html的base标签可能对JS代码不起作用。所以JS代码最好前面写上"/oa" */
			document.location.href = "${pageContext.request.contextPath}/dept/delete?deptno=" + dno;
		}
	}
</script>

		<h1 align="center">部门列表</h1>
		<hr >
		<table border="1px" align="center" width="50%">
			<tr>
				<th>序号</th>
				<th>部门编号</th>
				<th>部门名称</th>
				<th>操作</th>
			</tr>

			<c:forEach items="${deptList}" varStatus="deptStatus" var="dept">
				<tr>
					<td>${deptStatus.count}</td>
					<td>${dept.deptno}</td>
					<td>${dept.dname}</td>
					<td>
						<a href="javascript:void(0)" onclick="del(${dept.deptno})">删除</a>
						<a href="dept/detail?f=edit&dno=${dept.deptno}">修改</a>
						<a href="dept/detail?f=detail&dno=${dept.deptno}">详情</a>
					</td>
				</tr>
			</c:forEach>

		</table>
		
		<hr >
		<a href="add.jsp">新增部门</a>
		
	</body>
</html>

相关文章:

  • STL新增内容
  • flutter 曲线学习 使用第三方插件实现左右滑动
  • 厘米级定位赋能智造升级:品铂科技UWB技术驱动工厂全流程自动化与效能跃升”
  • Boost库中的谓词函数
  • 基于大模型的室间隔缺损手术全流程预测与方案研究报告
  • 蹊跷的崩溃:CoreData 数据保存时提示“不可接受类型”(Unacceptable type)
  • k8s常用总结
  • C++刷题(四):vector
  • 没有数据湖?可观测性也许不再有效!
  • 透视飞鹤2024财报:如何打赢奶粉罐里的科技战?
  • deepseek对IBM MQ错误日志分析
  • java项目挂机自动重启操作指南
  • STM32八股【5】----- TIM定时器
  • 堆叠虚拟化2
  • 界面自适应new 使用postcss-pxtorem
  • FreeRTOS 知识点总结(二):同步机制与应用场景
  • 如何在JMeter中配置断言,将非200状态码视为测试成功
  • java 洛谷题单【数据结构1-4】图的基本应用
  • 15:00开始面试,15:08就出来了,问的问题有点变态。。。
  • 射频功率放大器保护电路简略
  • 穆迪下调美国主权信用评级
  • 时隔三年,俄乌直接谈判重启
  • 江西3人拟提名为县(市、区)长候选人
  • 外交部:国际社会广泛理解和支持中方不同意台参加世卫大会的决定
  • 中巴续签双边本币互换协议,进一步深化金融战略合作
  • 泽连斯基批准美乌矿产协议