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

JavaWeb项目(纯Servlet+JSP+前端三大件)入门(从0开始)

文章目录

  • 一、Students2025项目(用页面展示学生列表---模拟数据)
    • 原始Servlet+JSP架构项目初步搭建
    • 项目结构:
    • 1.数据库导入数据
    • 2.创建Java版本的Web项目
    • 3. 控制层:servlet
      • StudentServlet
    • 4. 数据模型
      • Student
    • 5. Service层
      • 定义接口:StudentService
      • 定义实现类 StudentServiceImpl
    • 6. 数据仓库(对数据进行读取):Dao层
      • 定义接口:StudentDao
      • 定义具体的实现类 :StudentDaoImpl
    • 7. 页面展示:jsp部分
    • 8. 最终的pom.xml配置文件
    • 项目运行结果
  • 二、Students2025项目重构优化(用页面展示学生列表---连接数据库读取真实数据)
  • 真实查数据--从数据库真实读取数据
    • 1.引入依赖(驱动)
    • 2.创建数据库池:common:Global
    • 3.修改Dao层下的 实现类:StudentDaoImpl
  • 三、Students2025项目美化页面
    • CSS和JS静态资源的引入和使用
  • 四、Students2025项目重构优化代码
    • 1.重构成一次创建对象和接口类型
    • 2. 进一步优化:写工具类只获取一次StudentServiceImpl实例
    • 3. 动态加载并展示学生数据---AJAX(借助于js实现的)异步调用
      • 关键特点:
      • 系统联动:
      • 典型应用场景:

一、Students2025项目(用页面展示学生列表—模拟数据)

原始Servlet+JSP架构项目初步搭建

jsp项目已被淘汰,在此学习目的是了解未来学习的新技术的底层原理

项目结构:

在这里插入图片描述

项目结构介绍:
目前阶段只完成了初始化的后端搭建,实现从本地数据库获取数据在浏览器显示
请求处理流程
HTTP请求 → StudentsServlet → StudentServiceImpl → StudentDaoImpl → Global → 数据库
响应方向:
数据库 → Global → StudentDaoImpl → StudentServiceImpl → StudentsServlet → JSP → HTTP响应

各层职责MVC: DAO层:负责数据获取和初步封装(M)

Service层:理论上应包含业务逻辑处理,但当前示例简单透传

Servlet:控制流程和准备视图数据(C)

JSP:专注于数据展示(V)

响应与请求路径差异:请求是"控制流"向下传递响应是"数据流"向上返回最终在表示层完成数据到视图的转换

项目书写顺序:

数据库设计 → 模型类 → DAO层 → Service层 → Controller → 视图↑______________________________________|自底向上,逐层依赖

项目大体运行流程为Global->StudentServlet->StudentService->StudentDao,model的Student类为数据模型
接下来按项目大体运行流程的顺序介绍各部分

1.数据库导入数据

我们将数据全都写在了sql文件里面,现在直接将数据导入到数据库里面

首先在navicat里面建立数据库students2025:

在这里插入图片描述
建立完成后,直接导入我们准备好的数据:右键选择运行SQL文件
在这里插入图片描述在这里插入图片描述
出现下面的加载信息:successfully证明我们已经成功导入数据了

在这里插入图片描述

2.创建Java版本的Web项目

在这里插入图片描述
创建完成后,要做的是设置编码格式(一定要先配置编码格式),配置maven,查看修改pom.xml引入项目需要的依赖。

配置编码格式:
在这里插入图片描述
配置maven:使用的是本地的maven
在这里插入图片描述

查看pom.xml文件,加入我们所需要的相关配置:引入servlet和jsp依赖 ,引入lombok依赖(实体类里面用到的)。引入后一定要保存刷新maven

在这里插入图片描述

代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.situ</groupId><artifactId>students20252</artifactId><version>1.0-SNAPSHOT</version><name>students20252</name><!-- 一定要修改打包方式为war包  --><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.target>21</maven.compiler.target><maven.compiler.source>21</maven.compiler.source><junit.version>5.11.0</junit.version></properties><dependencies><!-- 引入依赖:jsp的并设置生效范围scope --><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-jsp-api</artifactId><version>11.0.0</version><!--  作用域:由外部提供 ,在项目打包的时候不把这两个打包,由外部(tomcat)提供 --><scope>provided</scope></dependency><!--   servlet的依赖     --><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-servlet-api</artifactId><version>11.0.9</version><scope>provided</scope></dependency><!--  lombok      --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.38</version></dependency></dependencies><build><!--   插件:目前默认的是下面这些      --><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>3.4.0</version></plugin></plugins></build>
</project>

创建并配置项目完成,接下来真正写项目
项目采用MVC三层架构的模式,可以先写出包,然后将对应层次的代码放在指定的包下:

在这里插入图片描述

3. 控制层:servlet

StudentServlet

package com.situ.students20252.servlet;import com.situ.students20252.model.Student;
import com.situ.students20252.service.impl.StudentServiceImpl;
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 java.io.IOException;
import java.util.List;//1.配置访问路径
@WebServlet("/admin/student/*")
public class StudentServlet extends HttpServlet {//2.重写doget和dopost方法(因为浏览器只支持这两个请求方式)@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//3.想用页面展示学生列表,但是servlet写网页不方便,所以请求转发去jsp(jsp写网页很方便,且用户无感知)String action = req.getPathInfo();//3.1先获取用户输入的*对应的是什么.getPathInfo()是专门获取*是什么的方法if("/list".equals(action)){//3.2进行判断,技巧:用已知的字符串去比较,放在前面.这样绝对不会出现空的情况异常//4.下面应该处理业务逻辑部分了,但是业务逻辑通常不写在这里,因为servlet是控制器 ,负责接收请求调度请求 ,在这里写业务逻辑会很臃肿//5.所以去service里面写,然后这里只需要用面向对象的思想new对象调用方法即可:前面一系列相关方法写完后:StudentServiceImpl studentService = new StudentServiceImpl();List<Student> students = studentService.findAll();//结果保存在students里面//6.把调用方法之后的结果存到域对象中 :第一个参数("students")是这个数据的标识符;// 第二个参数(students)是真正存储的数据。req.setAttribute("students",students);//3.3:请求转发的具体实现:由jsp页面负责展示给浏览器内容--去看(写)list.jsp页面req.getRequestDispatcher("/WEB-INF/jsp/student/list.jsp").forward(req,resp);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//项目中需要写的地方很多,加上这个to do可以防止我们遗漏东西,方便后续项目的完成:View--Tool Windows---todo//todo}
}

作用:
处理http请求,
用户访问URL: /admin/student/list,if判断用户输入地址是不是/admin/student//list,如果是,创建StudentServiceImpl对象,调用findall方法,将获取到的数据存到List中,然后将名为students的集合存放到request请求域中,然后request调用请求转发方法将students转发到/WEB-INF/jsp/student/list.jsp。

4. 数据模型

Student

model下的Student类:数据模型类,用来保存数据,每一行student的数据存一行。

package com.situ.students20252.model;import lombok.Getter;
import lombok.Setter;import java.time.LocalDate;//使用lombok:在pom.xml里面引入依赖,添加getter和setter方法。虽然看不见,但是编译的时候会编译出来
@Getter
@Setter
public class Student {//数据模型(每一行student的数据存一行),用来保存数据://属性的名字尽量要和数据库里面的名字对应,同时遵循java的命名规范private Integer id;private String  stuId;private String name;private String pinyin;private String sex;private String qq;private String email;private String phone;private String wechat;private LocalDate birthday;
}

数据模型建立好(实体类)之后,才可以编写业务逻辑(有了实体类才能new对象,写方法)):

5. Service层

定义接口:StudentService

定义接口:无需关注它的具体实现

package com.situ.students20252.service;
import com.situ.students20252.model.Student;
import java.util.List;
public interface StudentService {//定义查询功能的方法:findAll:List<Student> findAll();
}

定义实现类 StudentServiceImpl

我们要做的是查询所有数据的业务逻辑,如何查询?从数据库中进行查询读取,关于数据的是dao层处理,所以我们只需要用面向对象的思维交给dao层处理,我们这里只需要 new dao层的对象然后调用方法即可。

package com.situ.students20252.service.impl;import com.situ.students20252.dao.impl.StudentDaoImpl;
import com.situ.students20252.model.Student;
import com.situ.students20252.service.StudentService;import java.util.List;public class StudentServiceImpl implements StudentService {@Overridepublic List<Student> findAll() {//实现业务逻辑:如何读取?从数据库中查---交给dao层处理//我们本层只需要用面向对象的思维:new实现类的对象,调用方法即可:StudentDaoImpl studentDao = new StudentDaoImpl();List<Student> students = studentDao.findAll();//返回结果:return students;}
}

6. 数据仓库(对数据进行读取):Dao层

定义接口:StudentDao

同样也是只定义接口,不实现 ,交给实现类去实现

package com.situ.students20252.dao;
import com.situ.students20252.model.Student;
import java.util.List;
public interface StudentDao {List<Student> findAll();
}

定义具体的实现类 :StudentDaoImpl

在这里真正实现方法:从数据库中读取具体的数据并返回结果。

这里采用模拟数据的方法,先不从数据库里面调取数据:先模拟一个假的数据:

package com.situ.students20252.dao.impl;import com.situ.students20252.dao.StudentDao;
import com.situ.students20252.model.Student;import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;public class StudentDaoImpl implements StudentDao {@Overridepublic List<Student> findAll() {//具体实现查询所有数据的功能://模拟假的数据:List<Student> list = new ArrayList<>();Student student = new Student();student.setId(1);student.setName("张三");student.setPinyin("zhangsan");student.setSex("男");student.setQq("2344");student.setBirthday(LocalDate.of(2000,1,2));student.setWechat("123");student.setPhone("123334");list.add(student);Student student2 = new Student();student2.setId(2);student2.setName("李四");student2.setPinyin("lisi");student2.setSex("男");student2.setQq("1212");student2.setBirthday(LocalDate.of(2000,1,2));student2.setWechat("456");student2.setPhone("4111");list.add(student2);Student student3 = new Student();student3.setId(3);student3.setName("张三五");student3.setPinyin("zhangsanwu");student3.setSex("男");student3.setQq("2344");student3.setBirthday(LocalDate.of(2000,1,2));student3.setWechat("123");student3.setPhone("123334");list.add(student);return  list;}
}

7. 页面展示:jsp部分

list…jsp:

<%--Created by IntelliJ IDEA.User: LenovoDate: 2025/7/25Time: 20:27To change this template use File | Settings | File Templates.
--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%--2.引入JSTL标签库:固定语法--%>
<%@ taglib prefix="c" uri="jakarta.tags.core" %><html>
<head><title>学生列表页面</title>
</head>
<body>
<%--JSP用于给浏览器的展示--%>
<%--1.表格,展示给浏览器--%>
<table id="tbl">
<%--    表头--%><thead><tr><th>ID</th><th>学号</th><th>姓名</th><th>拼音</th><th>性别</th><th>出生日期</th><th>手机号</th><th>微信号</th><th>邮箱</th><th>QQ</th></tr></thead><%--表的主体--%><tbody><%--2.要写循环去遍历,也就是java代码 ,但是程序员不喜欢在前端页面里面写后端的java代码,
所以引入了:JSTL+EL使用标签写逻辑(需要在pom.xml文件导入依赖)--%><%--3.(是前面所有的代码业务逻辑全部写完才加的这个遍历数据部分)循环遍历数据展示出来(把数据存到请求域中,从请求域中取出来展示).items="${students}:从请求域中取数据students var="s"是起个名字叫s --%><c:forEach items="${students}" var="s"><tr><td>${s.id}</td><td>${s.stuId}</td><td>${s.name}</td><td>${s.pinyin}</td><td>${s.sex}</td><td>${s.birthday}</td><td>${s.phone}</td><td>${s.wechat}</td><td>${s.email}</td><td>${s.qq}</td></tr></c:forEach></tbody>
</table>
</body>
</html>

至此,我们已经实现了采用模拟数据的方式,将数据显示在浏览器页面上。

8. 最终的pom.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.situ</groupId><artifactId>students20252</artifactId><version>1.0-SNAPSHOT</version><name>students20252</name><!-- 一定要修改打包方式为war包  --><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.target>21</maven.compiler.target><maven.compiler.source>21</maven.compiler.source><junit.version>5.11.0</junit.version></properties><dependencies><!-- 引入依赖:jsp的并设置生效范围scope --><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-jsp-api</artifactId><version>11.0.0</version><!--  作用域:由外部提供 ,在项目打包的时候不把这两个打包,由外部(tomcat)提供 --><scope>provided</scope></dependency><!--   servlet的依赖     --><dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-servlet-api</artifactId><version>11.0.9</version><scope>provided</scope></dependency><!--  lombok      --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.38</version></dependency><!-- JSTL+EL依赖:--><!-- 接口:--><!-- https://mvnrepository.com/artifact/jakarta.servlet.jsp.jstl/jakarta.servlet.jsp.jstl-api --><dependency><groupId>jakarta.servlet.jsp.jstl</groupId><artifactId>jakarta.servlet.jsp.jstl-api</artifactId><version>3.0.2</version></dependency><!--实现:--><!-- https://mvnrepository.com/artifact/org.glassfish.web/jakarta.servlet.jsp.jstl --><dependency><groupId>org.glassfish.web</groupId><artifactId>jakarta.servlet.jsp.jstl</artifactId><version>3.0.1</version></dependency></dependencies><build><!--   插件:目前默认的是下面这些      --><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>3.4.0</version></plugin></plugins></build>
</project>

项目运行结果

总的项目结构以及项目运行结果如图所示:
在这里插入图片描述

项目运行结果:

在这里插入图片描述

二、Students2025项目重构优化(用页面展示学生列表—连接数据库读取真实数据)

上述结果我们已经看到,但是数据都是模拟数据且页面比较简陋,所以接下来我们对项目进行重构优化

真实查数据–从数据库真实读取数据

1.引入依赖(驱动)

在pom.xml文件中引入mysql驱动,有驱动才能对mysql进行操作

  <!--mysql驱动--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.4.0</version></dependency>

使用spring jdbc工具库(单纯的一个库):专门用于java操作数据库的库:

 <!--spring jdbc,可以方便的进行数据库操作--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.2.8</version></dependency>

加入后刷新 maven

2.创建数据库池:common:Global

Common层–公共配置(数据库…)

Java项目连接数据库 Global文件:

package com.situ.students20252.common;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import javax.sql.DataSource;
import java.sql.Driver;
import java.sql.SQLException;public class Global {//连接数据库://1.四大参数:// 驱动名:固定的private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";//数据库连接字符串(固定的规范):指定连接哪个服务器,哪个端口,哪个数据库 名字private static final String JDBC_URL = "jdbc:mysql://localhost:3306/students2025?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true";//用户名:private static final String JDBC_USERNAME = "root";//密码private static final String JDBC_PASSWORD = "123456";//2.定义方法:获取数据源 数据库连接池//定义成静态的,让他为空,这样可以只创建一次连接就行了private static DataSource dataSource = null;//DataSource是一个类 :专门连接数据库的,里面存储了一些数据库的连接,所以称为数据库连接池public static DataSource getDataSource() throws SQLException {if (dataSource == null) {Driver driver = new com.mysql.cj.jdbc.Driver();dataSource = new SimpleDriverDataSource(driver, JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD);}return dataSource;//返回数据源}//3.spring提供的jdbc模板操作类public static JdbcTemplate getJdbcTemplate() {try {//JdbcTemplate里面有很多数据库的操作方法,new对象直接使用return new JdbcTemplate(getDataSource());} catch (SQLException e) {throw new RuntimeException(e);}}
}

作用:
初始化数据库连接,创建数据库连接池,如果连接池为空,就按参数新建一个连接,JdbcTemplate 是 Spring JDBC 模块的核心类,通过get数据源创建 JdbcTemplate 实例,在StudentDao中被调用

数据库连接池
用于在应用程序启动时创建一定数量的数据库连接,
并将这些连接保存在一个"池"中,供应用程序随时取用。当应用程序需要与数据库交互时,
不是新建一个连接,而是从连接池中获取一个空闲连接,使用完毕后再归还给连接池,而不是关闭它。

3.修改Dao层下的 实现类:StudentDaoImpl

删除模拟数据,获取从数据库真正读取的数据:

package com.situ.students20252.dao.impl;import com.situ.students20252.common.Global;
import com.situ.students20252.dao.StudentDao;
import com.situ.students20252.model.Student;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;import java.util.List;public class StudentDaoImpl implements StudentDao {@Overridepublic List<Student> findAll() {//具体实现查询所有数据的功能://1.从本地数据库获取数据:要用到JdbcTemplate类,因为这个类里面有很多操作数据库的方法,用起来很方便JdbcTemplate jdbcTemplate = Global.getJdbcTemplate();//2.进行数据查询:sql语句:查询的列名必须与数据库中的列名完全一致String sql = "select id,stu_id,name,sex,birthday,pinyin,phone,email,qq,wechat from t_student limit 20";//3.将读取的数据转变成列表//行映射器BeanPropertyRowMapper:把数据库中的每一行映射成一个对象(javabean的属性)RowMapper<Student> rowMapper = new BeanPropertyRowMapper<Student>(Student.class);//4.执行查询操作List<Student> students = jdbcTemplate.query(sql, rowMapper);return students;}
}

从数据库展示结果 :

在这里插入图片描述

三、Students2025项目美化页面

基于一和二部分,我们已经实现了从数据库真实的读取数据并在浏览器页面显示,接下来进行对项目的重构优化以及页面美化。

CSS和JS静态资源的引入和使用

静态资源一般放到webapp下,创建文件夹:assets表示放静态资源

在assets下面建立lib:存放库文件:比如jquery

分门别类的放文件。
在这里插入图片描述
引入方式:在jsp前端页面里面引入:

list.js文件:

$(()=>{console.log("ddd");
});

list.css

@charset "UTF-8";#tbl {border-collapse: collapse;width: 100%;
}#tbl tr>td{border: 1px solid #666;padding: 6px 10px;
}

在jsp前端显示页面中引入css和js:

<%--Created by IntelliJ IDEA.User: LenovoDate: 2025/7/25Time: 20:27To change this template use File | Settings | File Templates.
--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%--2.引入JSTL标签库:固定语法--%>
<%@ taglib prefix="c" uri="jakarta.tags.core" %><%--获取上下文路径:pageContext页面域:仅在当前页面的类有效--%>
<c:set var="ctx" value="${pageContext.request.contextPath}"/><html>
<head>
<%--    base标记:意思是整个页面基于什么生效--%><base href="${ctx}/"><title>学生列表页面</title>
<%--    引入css--%><link rel="stylesheet" href="assets/modules/student/css/list.css">
<%--    引入js--%><script src="assets/lib/jquery/jquery-3.7.1.min.js"></script><script src="assets/modules/student/js/list.js"></script>
</head>
<body>
<%--JSP用于给浏览器的展示--%>
<%--1.表格,展示给浏览器--%>
<table id="tbl">
<%--    表头--%><thead><tr><th>ID</th><th>学号</th><th>姓名</th><th>拼音</th><th>性别</th><th>出生日期</th><th>手机号</th><th>微信号</th><th>邮箱</th><th>QQ</th></tr></thead><%--表的主体--%><tbody><%--2.要写循环去遍历,也就是java代码 ,但是程序员不喜欢在前端页面里面写后端的java代码,
所以引入了:JSTL+EL使用标签写逻辑(需要在pom.xml文件导入依赖)--%><%--3.(是前面所有的代码业务逻辑全部写完才加的这个遍历数据部分)循环遍历数据展示出来(把数据存到请求域中,从请求域中取出来展示).items="${students}:从请求域中取数据students var="s"是起个名字叫s --%><c:forEach items="${students}" var="s"><tr><td>${s.id}</td><td>${s.stuId}</td><td>${s.name}</td><td>${s.pinyin}</td><td>${s.sex}</td><td>${s.birthday}</td><td>${s.phone}</td><td>${s.wechat}</td><td>${s.email}</td><td>${s.qq}</td></tr></c:forEach></tbody>
</table>
</body>
</html>

项目运行显示结果

在这里插入图片描述

四、Students2025项目重构优化代码

1.重构成一次创建对象和接口类型

servlet部分

之前的源代码部分:

package com.situ.students20252.servlet;import com.situ.students20252.model.Student;
import com.situ.students20252.service.impl.StudentServiceImpl;
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 java.io.IOException;
import java.util.List;//1.配置访问路径
@WebServlet("/admin/student/*")
public class StudentServlet extends HttpServlet {//2.重写doget和dopost方法(因为浏览器只支持这两个请求方式)@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//3.想用页面展示学生列表,但是servlet写网页不方便,所以请求转发去jsp(jsp写网页很方便,且用户无感知)String action = req.getPathInfo();//3.1先获取用户输入的*对应的是什么.getPathInfo()是专门获取*是什么的方法if("/list".equals(action)){//3.2进行判断,技巧:用已知的字符串去比较,放在前面.这样绝对不会出现空的情况异常//4.下面应该处理业务逻辑部分了,但是业务逻辑通常不写在这里,因为servlet是控制器 ,负责接收请求调度请求 ,在这里写业务逻辑会很臃肿//5.所以去service里面写,然后这里只需要用面向对象的思想new对象调用方法即可:前面一系列相关方法写完后:StudentServiceImpl studentService = new StudentServiceImpl();List<Student> students = studentService.findAll();//结果保存在students里面//6.把调用方法之后的结果存到域对象中 :第一个参数("students")是这个数据的标识符;// 第二个参数(students)是真正存储的数据。req.setAttribute("students",students);//3.3:请求转发的具体实现:由jsp页面负责展示给浏览器内容--去看(写)list.jsp页面req.getRequestDispatcher("/WEB-INF/jsp/student/list.jsp").forward(req,resp);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//项目中需要写的地方很多,加上这个to do可以防止我们遗漏东西,方便后续项目的完成:View--Tool Windows---todo//todo}
}

对于这个部分的创建实例对象部分:new StudentServiceImpl();业务类是 没有状态的,没有成员属性的,是用于执行具体操作的,所以创建100个和创建一个是一样的。

如果一个类型没有状态(状态:每个人姓名年龄身高体重这种)没有属性,那么创建多少个都一样。

重构的部分:

1.我们目前的代码是每次发请求都会创建对象,创建对象会开辟内存、占用时间的 ,是没有必要创建那么多次的,所以进行代码重构,只需要创建一次就行。

2.面向接口编程:我们创建对象的类型一般定义成接口类型 (后续具体的实现类改变我们只需要改变后面名字即可,前面的接口类型不用改变了(接口多态表达 ))
定义成接口的原因:方便程序的扩展。比如需要更换实现类,我们是使用接口调用的,不关心实现类的实现,所以只需要更换后面new对象的部分就行了,其余的地方都不用动。

重构后的代码:StudentServlet

package com.situ.students20252.servlet;import com.situ.students20252.model.Student;
import com.situ.students20252.service.StudentService;
import com.situ.students20252.service.impl.StudentServiceImpl;
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 java.io.IOException;
import java.util.List;//1.配置访问路径
@WebServlet("/admin/student/*")
public class StudentServlet extends HttpServlet {//代码重构 :这样定义只执行一次,且修改成接口类型private StudentService studentService = new StudentServiceImpl();//2.重写doget和dopost方法(因为浏览器只支持这两个请求方式)@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//3.想用页面展示学生列表,但是servlet写网页不方便,所以请求转发去jsp(jsp写网页很方便,且用户无感知)String action = req.getPathInfo();//3.1先获取用户输入的*对应的是什么.getPathInfo()是专门获取*是什么的方法if("/list".equals(action)){//3.2进行判断,技巧:用已知的字符串去比较,放在前面.这样绝对不会出现空的情况异常//4.下面应该处理业务逻辑部分了,但是业务逻辑通常不写在这里,因为servlet是控制器 ,负责接收请求调度请求 ,在这里写业务逻辑会很臃肿//5.所以去service里面写,然后这里只需要用面向对象的思想new对象调用方法即可:前面一系列相关方法写完后:List<Student> students = studentService.findAll();//结果保存在students里面//6.把调用方法之后的结果存到域对象中 :第一个参数("students")是这个数据的标识符;// 第二个参数(students)是真正存储的数据。req.setAttribute("students",students);//3.3:请求转发的具体实现:由jsp页面负责展示给浏览器内容--去看(写)list.jsp页面req.getRequestDispatcher("/WEB-INF/jsp/student/list.jsp").forward(req,resp);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//项目中需要写的地方很多,加上这个to do可以防止我们遗漏东西,方便后续项目的完成:View--Tool Windows---todo//todo}
}

同理改变service层部分也这样设置,使用接口调用

StudentServiceImpl:

package com.situ.students20252.service.impl;import com.situ.students20252.dao.StudentDao;
import com.situ.students20252.dao.impl.StudentDaoImpl;
import com.situ.students20252.model.Student;
import com.situ.students20252.service.StudentService;import java.util.List;public class StudentServiceImpl implements StudentService {private StudentDao studentDao = new StudentDaoImpl();@Overridepublic List<Student> findAll() {//实现业务逻辑:如何读取?从数据库中查---交给dao层处理//我们本层只需要用面向对象的思维:new实现类的对象,调用方法即可:List<Student> students = studentDao.findAll();//返回结果:return students;}
}

同样Dao层:

只new一次对象的代码重构:

package com.situ.students20252.dao.impl;import com.situ.students20252.common.Global;
import com.situ.students20252.dao.StudentDao;
import com.situ.students20252.model.Student;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;import java.util.List;public class StudentDaoImpl implements StudentDao {//代码重构,放在外面,用private修饰,只new一次对象。// 行映射器BeanPropertyRowMapper:把数据库中的每一行映射成一个对象(javabean的属性)private RowMapper<Student> rowMapper = new BeanPropertyRowMapper<Student>(Student.class);@Overridepublic List<Student> findAll() {//具体实现查询所有数据的功能://1.从本地数据库获取数据:要用到JdbcTemplate类,因为这个类里面有很多操作数据库的方法,用起来很方便JdbcTemplate jdbcTemplate = Global.getJdbcTemplate();//2.进行数据查询:sql语句:查询的列名必须与数据库中的列名完全一致String sql = "select id,stu_id,name,sex,birthday,pinyin,phone,email,qq,wechat from t_student limit 20";//3.将读取的数据转变成列表//4.执行查询操作List<Student> students = jdbcTemplate.query(sql, rowMapper);return students;}
}

数据模型model层是有状态的,不用重构 。

2. 进一步优化:写工具类只获取一次StudentServiceImpl实例

程序员new对象容易失控,保证只有一个StudentServiceImpl实例----也就是单例模式
在这里插入图片描述

解决方案:

写spring的工具类 ,保证只有一个StudentServiceImpl实例。

utils包下写一个工厂类:

package com.situ.students20252.utils;import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;public class BeanFactory {//工具类 :只获取一次StudentServiceImpl实例//spring眼里没有状态的都是bean,理解原理就行。只获取一次StudentServiceImpl实例:private static Map<Class<?>, Object> beanMap = new HashMap<Class<?>,Object>();//反射思想public static <T> T getBean(Class<T> clazz){//先去beanMap里面取,如果bean是空才会创建。// 单例实现原理:通过静态Map缓存实例,每次调用优先返回缓存对象。T t = (T) beanMap.get(clazz);if(t!=null){return t;}try {//获取无参的构造函数:Constructor<T> constructor = clazz.getDeclaredConstructor();//创建实例对象:t =  constructor.newInstance();beanMap.put(clazz,t);return t;} catch (Exception e) {throw new RuntimeException(e);}}
}

然后我改变 创建对象的方式实现只获取一次实例:用final修饰+getBean获取

 private final StudentService studentService = BeanFactory.getBean(StudentServiceImpl.class);

StudentServlet:改变的只有创建对象的方式

package com.situ.students20252.servlet;import com.situ.students20252.model.Student;
import com.situ.students20252.service.StudentService;
import com.situ.students20252.service.impl.StudentServiceImpl;
import com.situ.students20252.utils.BeanFactory;
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 java.io.IOException;
import java.util.List;//1.配置访问路径
@WebServlet("/admin/student/*")
public class StudentServlet extends HttpServlet {//5.1代码重构 :这样定义只执行一次,且修改成接口类型// private StudentService studentService = new StudentServiceImpl();//5.2继续重构:使用工厂类只创建一次对象:getBeanprivate final StudentService studentService = BeanFactory.getBean(StudentServiceImpl.class);//2.重写doget和dopost方法(因为浏览器只支持这两个请求方式)@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//3.想用页面展示学生列表,但是servlet写网页不方便,所以请求转发去jsp(jsp写网页很方便,且用户无感知)String action = req.getPathInfo();//3.1先获取用户输入的*对应的是什么.getPathInfo()是专门获取*是什么的方法if("/list".equals(action)){//3.2进行判断,技巧:用已知的字符串去比较,放在前面.这样绝对不会出现空的情况异常//4.下面应该处理业务逻辑部分了,但是业务逻辑通常不写在这里,因为servlet是控制器 ,负责接收请求调度请求 ,在这里写业务逻辑会很臃肿//5.3所以去service里面写,然后这里只需要用面向对象的思想new对象调用方法即可:前面一系列相关方法写完后:List<Student> students = studentService.findAll();//结果保存在students里面//6.把调用方法之后的结果存到域对象中 :第一个参数("students")是这个数据的标识符;// 第二个参数(students)是真正存储的数据。req.setAttribute("students",students);//3.3:请求转发的具体实现:由jsp页面负责展示给浏览器内容--去看(写)list.jsp页面req.getRequestDispatcher("/WEB-INF/jsp/student/list.jsp").forward(req,resp);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//项目中需要写的地方很多,加上这个to do可以防止我们遗漏东西,方便后续项目的完成:View--Tool Windows---todo//todo}
}

同理,其他层的实现也需要改动,也是只改动创建对象的方式 :

service层改变:

//创建对象的方式 private  final StudentDao studentDao = BeanFactory.getBean(StudentDaoImpl.class);

在这里插入图片描述

同理Dao层改变
同样用final修饰+Bean

private final RowMapper<Student> rowMapper = new BeanPropertyRowMapper<Student>(Student.class)

至此实现了创建一次对象的代码重构

3. 动态加载并展示学生数据—AJAX(借助于js实现的)异步调用

现在的数据是直接显示在浏览器上面的,我们项目通常需要只先展示静态的比如表格表头这种,动态资源用户请求再显示 (先加载静态再加载动态))

所以使用AJAX异步调用进一步优化项目

AJAX = Asynchronous (异步) JavaScript and XML(异步的JavaScript和XML);
同步 — 客户端必须等待服务器端的响应,在等待的期间客户端不能做其他操作;
异步 — 客户端不需要等待服务器端的响应,在服务器处理请求的过程中,客户端可以进行其他的操作。
AJAX是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术;
通过在后台与服务器进行少量数据交换,AJAX可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新;
传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页;
提升用户体验。

我们之前是在list.jsp里面写了forEach循环直接将结果显示在浏览器上面 ,所以我们删除遍历的部分的代码,用AJAX异步调用的方式显示数据到浏览器:

在这里插入图片描述在这里插入图片描述
删除后的效果
在这里插入图片描述
接下来我们要使用AJAX进行数据的展示 ,用户请求了再展示

AJAX借助于JS实现,所以我们找到js代码,在js部分书写 :
list.jsp里面添加上下文路径:

<%--Created by IntelliJ IDEA.User: LenovoDate: 2025/7/25Time: 20:27To change this template use File | Settings | File Templates.
--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%--2.引入JSTL标签库:固定语法--%>
<%@ taglib prefix="c" uri="jakarta.tags.core" %><%--获取上下文路径:pageContext页面域:仅在当前页面的类有效--%>
<c:set var="ctx" value="${pageContext.request.contextPath}"/><html>
<head>
<%--    base标记:意思是整个页面基于什么生效--%><base href="${ctx}/"><title>学生列表页面</title>
<%--    引入css--%><link rel="stylesheet" href="assets/modules/student/css/list.css">
<%--    引入js--%><script src="assets/lib/jquery/jquery-3.7.1.min.js"></script>
<%--    上下文路径:在HTML(或JSP、Thymeleaf等模板)中使用的脚本片段,
它的作用是将服务器端的一个变量 `${ctx}` 的值赋给JavaScript中的常量 `ctx`--%><script>const ctx ="${ctx}";</script><script src="assets/modules/student/js/list.js"></script></head>
<body>
<%--JSP用于给浏览器的展示--%>
<%--1.表格,展示给浏览器--%>
<table id="tbl">
<%--    表头--%><thead><tr><th>ID</th><th>学号</th><th>姓名</th><th>拼音</th><th>性别</th><th>出生日期</th><th>手机号</th><th>微信号</th><th>邮箱</th><th>QQ</th></tr></thead><%--表的主体--%><tbody><%--  用AJAX异步调用的方式显示数据到浏览器: 见 js内容 --%></tbody>
</table>
</body>
</html>

在这里插入图片描述

list.js里面:

// 通过AJAX从服务器获取学生列表数据,并将数据动态填充到HTML表格中$(() => {// 页面加载完成后自动调用findAll函数。findAll();
});//查询所有数据并显示:
function findAll() {//发出异步请求,响应的不是页面$.ajax({//findAll函数通过AJAX发送POST请求到服务器的`/admin/student/list`接口url: ctx + "/admin/student/list",//ctx是上下文路径method: "post",//回调函数 callbacksuccess: function (resp) {$("#tbl>tbody").empty();//清空tbody里面的内容for (let i = 0; i < resp.length; i++) {//遍历接收的数据let stu = resp[i];//一个学生对象//将接收到的数据写入tbodylet $tr = $("<tr>");$tr.append("<td>"+stu.id+"</td>");$tr.append("<td>"+stu.stuId+"</td>");$tr.append("<td>"+stu.name+"</td>");$tr.append("<td>"+stu.pinyin+"</td>");$tr.append("<td>"+stu.sex+"</td>");$tr.append("<td>"+stu.birthday+"</td>");$tr.append("<td>"+stu.phone+"</td>");$tr.append("<td>"+stu.wechat+"</td>");$tr.append("<td>"+stu.email+"</td>");$tr.append("<td>"+stu.qq+"</td>");$("#tbl>tbody").append($tr);}}});}

上述代码的说明:

这段代码是一个典型的前端数据动态加载和展示功能,主要用于学生信息管理系统。其核心功能是在页面加载完成后,通过AJAX技术从服务器获取学生列表数据,并将这些数据动态填充到HTML表格中展示。
核心总结

  1. 触发时机:页面加载完成后自动执行。
  2. 数据获取:使用AJAX POST请求访问后端接口 /admin/student/list 获取JSON格式的学生数据。
  3. 数据渲染
    • 清空表格现有数据($("#tbl>tbody").empty())。
    • 遍历每个学生对象,动态创建表格行(<tr>)和单元格(<td>)。
    • 将学生信息按字段填入单元格,并将行添加到表格中。
  4. 路径处理:依赖全局变量 ctx(项目上下文路径)构建完整请求URL。

关键特点:

  • 异步加载:不刷新页面即可更新数据。
  • 动态渲染:根据数据实时生成表格内容。
  • 解耦设计:前后端分离,后端只提供数据接口,前端负责展示。

系统联动:

  • 后端依赖:需要后端实现 /admin/student/list 接口返回学生数据数组。
  • 前端依赖:HTML中需存在ID为 tbl 的表格(含<tbody>)。
  • 全局变量:需要预先定义 ctx 变量表示项目根路径。

典型应用场景:

  • 学生信息管理系统的首页数据加载。
  • 管理员查看所有学生信息的场景。
  • 需要实时更新学生数据的任何页面。

🎯 核心功能
实现学生数据的异步加载和动态展示:

页面加载时自动触发数据请求

从服务器获取学生列表数据(JSON格式)

将数据动态渲染到HTML表格中
⚙️ 工作机制
在这里插入图片描述

🔗 系统关联
依赖后端接口:

请求地址:${ctx}/admin/student/list

要求返回:[{id:1, stuId:“S001”, name:“张三”, …}, …]

依赖页面结构:

html
<table id="tbl"><thead>...</thead><tbody> <!-- 动态内容区 --> </tbody>
</table>

依赖全局变量:

html
<script>const ctx = "/项目路径"; // 由服务器模板生成
</script>

⚠️ 注意事项
确保后端接口可用且返回正确JSON格式

保持前端字段名与后端数据一致

当学生数据量大时需考虑分页优化

添加加载状态提示和错误处理

一句话总结:这段代码实现了学生数据的无刷新动态加载和表格渲染,是前后端分离架构中的典型数据展示模块。


StudentServle里面:添加dopost请求体的内容:利用JSON:格式转换

序列化(对象 → JSON字符串)
将程序中的数据结构转换为JSON字符串的过程

反序列化(JSON字符串 → 对象)
将JSON字符串转换回程序可操作的数据结构

导JSON依赖:pom.xml

 <!--        json序列化和反序列化--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.57</version></dependency>
   @Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//项目中需要写的地方很多,加上这个to do可以防止我们遗漏东西,方便后续项目的完成:View--Tool Windows---todo//todoString pathInfo = req.getPathInfo();if ("/list".equals(pathInfo)) {//调用方法,查询数据,查询结果保存在了students里面List<Student> students = studentService.findAll();//设置响应内容为jsonresp.setContentType("application/json;charset=UTF-8");//将java对象序列化成json字符串.students就变成了json了String json = JSON.toJSONString(students);//想js文件响应数据(因为是js向他发的请求)PrintWriter out = resp.getWriter();//创建字符输出流out.write(json);out.flush();}}

至此实现动态加载数据

http://www.xdnf.cn/news/16432.html

相关文章:

  • 传统框架与减震楼盖框架地震动力响应分析与有限元模拟
  • HashMap的线程安全性 vs ConcurrentHashMap
  • cacti漏洞CVE-2022-46169复现
  • JavaScript 中 let 在循环中的作用域机制解析
  • 智慧校园(智能出入口控制系统,考勤,消费机,电子班牌等)标准化学校建设,加速业务规模发展,满足学校、家长及学生对智能化、便捷化校园管理的需求清单如下
  • MyBatis-Plus极速开发指南
  • Ⅹ—6.计算机二级综合题11---14套
  • Spring 生态创新应用:现代架构与前沿技术实践
  • 2025年-ClickHouse 高性能实时分析数据库(大纲版)
  • GaussDB 数据库架构师修炼(九) 逻辑备份实操
  • 学习笔记《区块链技术与应用》第二天 共识机制
  • ESP32学习笔记_Peripherals(4)——MCPWM基础使用
  • cha的操作
  • LP-MSPM0G3507学习--11ADC之二双通道高速DMA采样
  • 人工智能——插值方法、边缘填充、图像矫正、图像掩膜、ROI切割、图像添加水印、图像噪点消除
  • 九联UNT413AS_晶晨S905L3S芯片_2+8G_安卓9.0_线刷固件包
  • 蓝光中的愧疚
  • MySQL索引背后的B+树奥秘
  • Power Compiler:漏电功耗、内部功耗、切换功耗及其计算方式(NLPM)
  • 网络安全-机遇与挑战
  • 【内网穿透】使用FRP实现内网与公网Linux/Ubuntu服务器穿透项目部署多项目穿透方案
  • 在幸狐RV1106板子上用gcc14.2本地编译安装ssh客户端/服务器、vim编辑器、sl和vsftpd服务器
  • Apache Ranger 权限管理
  • 【优选算法】链表
  • 局域网 IP地址
  • 卡尔曼滤波器噪声方差设置对性能影响的仿真研究
  • 亚马逊广告策略:如何平衡大词和长尾词的效果?
  • JAVA_FIFTEEN_异常
  • 轮盘赌算法
  • CMake进阶: 检查函数/符号存在性、检查类型/关键字/表达式有效性和检查编译器特性