由于技术的更新迭代,现在主流技术是前后端分离,但是作用初学者来说对于深入理解web尤其是Servet的工作原理和流程(同时也是为了学习web框架的基础)深入掌握Setvlet的原理和工作流程是非常重要的,本文就是基于Jakarta下的(不同于javax)的Servet和Jsp的一个简单的应用,希望能够给初学web和想深入了解Servlet的同学有所帮助。
git地址:https://gitee.com/yangyang1110_admin/web-jsp-test.git
一、开发环境
JDK 17+
tomcat10
mysql8
idea2022+
maven3.9.+
二、技术栈
1、Servlet+Jsp
2、tomcat10+
3、泛型DAO
4、bootstrap(模态窗)
5、JSTL
三、项目实现
3.1 创建JakartaEE项目


3.2pom.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.example</groupId>
<artifactId>student_web</artifactId>
<version>1.0-SNAPSHOT</version>
<name>student_web</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<junit.version>5.9.1</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
3.3 测试

3.4 引入JSTL并测试
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<version>3.0.1</version>
</dependency>
index.jsp:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="hello-servlet">Hello Servlet</a>
<c:forEach begin="0" end="10" var="i">
<font color="blue">${i}</font>
</c:forEach>
</body>
</html>

3.5 对Servlet的封装
package com.example.student.web;
import com.example.student.service.UserService;
import com.example.student.service.factory.ServiceFactory;
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.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
/**
* @author TonySong
* @date 2025/8/7 0007
* @time 12:13
*/
public class BaseServlet extends HttpServlet {
protected static final String PREFIX="/WEB-INF/view/";
protected static final String SUFFIX=".jsp";
protected UserService userService= ServiceFactory.createUserService();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
String urlAnnotation=null;
//获取请求的Servlet对象
Class<?> servletClass = this.getClass();
// 直接获取类上的WebServlet注解(用于拼接url)
WebServlet servletAnnotation = servletClass.getAnnotation(WebServlet.class);
if(servletAnnotation != null) {
urlAnnotation = servletAnnotation.value()[0];
System.out.println(urlAnnotation);
}
/**
* 1. 获取method参数,它是用户想调用的方法
*/
String methodName = request.getParameter("action");
System.out.println("请求的方法:"+methodName);
Method method=null;
try {
//2. 通过反射获取方法
method = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException("没有找到对应的方法");
}
/**
*
* 3. 通过method对象来调用它
*/
try {
String result = (String)method.invoke(this, request, response);
System.out.println("方法的返回值是:"+result);
if(result != null && !result.trim().isEmpty()) {//如果请求处理方法返回不为空
int index = result.indexOf(":");//获取第一个冒号的位置 f:login.jsp
if(index == -1) {//如果没有冒号,使用转发
request.getRequestDispatcher(result).forward(request, response);
} else {//如果存在冒号
String start = result.substring(0, index);//分割出前缀
String path = result.substring(index + 1);//分割出路径
System.out.println("start:"+start);
System.out.println("path:"+path);
if("forward".equals(start)) {//前缀为forward表示转发
request.getRequestDispatcher(path).forward(request, response);
} else if("redirect".equals(start)) {//前缀为redirect表示重定向
String contextPath=request.getContextPath()+urlAnnotation+"?action=";
System.out.println(contextPath);
System.out.println(path);
response.sendRedirect(contextPath + path);
}
}
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
3.5.1 项目结构

3.5.2 测试Servlet
@WebServlet("/hello")
public class HelloServlet extends BaseServlet{
protected String list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello....list");
return "list.jsp";
}
protected String add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("hello....add");
return "redirect:list";
}
}
3.5.2 测试用jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>list</h1>
</body>
</html>
3.5.4 测试效果

控制台输出:

四、用户列表实现
4.1 新增依赖:
<!--Lombok 依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
</dependency>
<!--logback 日志依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.18</version>
</dependency>
<!--Mysql 依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.3.0</version>
</dependency>
4.2 日志
在resources下新增logback.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg %n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="com.example.student" level="DEBUG" additivity="false">
<appender-ref ref="Console"/>
</logger>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>
4.3 配置文件
resources下新增jdbc.properties,内容如下:
jdbc.username=mysql的账号
jdbc.password=mysql的密码
jdbc.url=jdbc:mysql://localhost:3306/book_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
jdbc.driver=com.mysql.cj.jdbc.Driver
4.4 JDBC的封装
4.4.1 通用接口
package com.example.student.dao;
import com.example.student.util.Pager;
import java.util.List;
/**
* 宋伟宁
* 2020/3/26
*/
public interface IGenericDao<T, PK extends Number> {
/**
* 执行聚合函数
*
* @param sql
* @return
*/
int executeScalre(String sql);
int executeScalre(String sql, Object object);
int executeScalre(String sql, Object[] args);
/**
* 列表
*
* @param select
* @param args
* @return
*/
List<T> findAll(String select, Object[] args);
List<T> findAll(String select, Object obj);
List<T> findAll(String select);
/**
* 查询对象
*
* @param select
* @param args
* @return
*/
T findObject(String select, Object[] args);
T findObject(String select, Object obj);
T findObject(String select);
int save(String insert, Object... args);
int delete(String delete, Integer id);
int update(String update, Object... args);
int getTotal(String sql, Object[] args);
int getTotal(String sql, Object object);
int getTotal(String sql);
/**
* 分页
*
* @param sql
* @param currentPage
* @param pagesize
* @return
*/
Pager<T> pagelist(String sql, String currentPage, int pagesize);
Pager<T> pagelist(String sql, String currentPage, int pagesize, Object obj);
Pager<T> pagelist(String sql, String currentPage, int pagesize, Object[] args);
}
4.4.2 通用数据操作类
package com.example.student.dao.impl;
import com.example.student.util.Pager;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* 通用数据库操作类
*
* @author 宋伟宁
* <p>
* 2019年10月3日下午10:40:55
*/
@Slf4j
public class BaseDao {
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
private static Connection connection;//连接
private static PreparedStatement pst;//预编译
private static ResultSet rs;//结果集
private static CallableStatement cstmt;//执行存储过程对象
private static Properties properties = new Properties();
private static String URL, DRIVER, USER, PWD;
private static int m;
//private static String JNDI;
/**
* 静态块读取配置文件(获取数据库连接属性)
* 获取数据库驱动
*/
static {
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties");
try {
properties.load(input);
DRIVER = properties.getProperty("jdbc.driver");
USER = properties.getProperty("jdbc.username");
PWD = properties.getProperty("jdbc.password");
URL = properties.getProperty("jdbc.url");
Class.forName(DRIVER);
log.debug("驱动:" + DRIVER + "账号:" + USER + "密码:" + PWD + "URL" + URL);
} catch (Exception e) {
log.debug("加载驱动失败。。。" + e.getMessage());
}
}
/**
* 通用的列表
*
* @param sql
* @param clazz
* @param args
* @return 泛型集合
*/
public static <T> List<T> findAll(String sql, Class<T> clazz, Object... args) {
log.debug("BaseDao..sql:" + sql);
List<T> list = new ArrayList<T>(20);
T t = null;
try {
pst = createPreparedStatement(sql, args);
rs = pst.executeQuery();
while (rs.next()) {
t = convert(clazz, rs);
list.add(t);
}
} catch (SQLException e) {
log.debug("BaseDao...findAll 错误。。。。" + e.getMessage());
} finally {
closeAll(rs, pst);
closeConnection();
}
return list;
}
/**
* 通用的分页
*
* @param sql
* @param clazz
* @param currentPage
* @param pagesize
* @param args
* @return
*/
public static <T> Pager<T> pageAll(String sql, Class<T> clazz, String currentPage, int pagesize, Object... args) {
log.debug("page sql:" + sql);
String total_sql = sql.replace("*", "count(*)").replace("limit ?,?", "").replace("LIMIT ?,?", "");
log.debug("total_sql: " + total_sql);
List<T> list = new ArrayList<T>(20);
T t = null;
Pager<T> pager = null;
try {
int total = executeScalare(total_sql, args);
pager = new Pager<>(total, currentPage, pagesize);
String sql_page = sql.replace("?,?", String.valueOf(pager.getOffset()) + "," + String.valueOf(pager.getPagesize()));
log.debug("BaseDao..sql:" + sql_page);
pst = createPreparedStatement(sql_page, args);
rs = pst.executeQuery();
while (rs.next()) {
t = convert(clazz, rs);
list.add(t);
}
pager.setDatas(list);
} catch (SQLException e) {
log.debug("BaseDao...分页查询 错误。。。。" + e.getMessage());
} finally {
closeAll(rs, pst);
closeConnection();
}
return pager;
}
public static void main(String[] args) {
}
/**
* 根据id或条件查询单个对象
*
* @param sql
* @param clazz
* @param args
* @return
*/
public static <T> T findByObject(String sql, Class<T> clazz, Object... args) {
T t = null;
log.debug("BaseDao..findByObject sql:" + sql);
try {
pst = createPreparedStatement(sql, args);
rs = pst.executeQuery();
if (rs.next()) {
t = convert(clazz, rs);
}
} catch (Exception e) {
log.debug("BaseDao findByObject。。。。错误!" + e.getMessage());
} finally {
closeAll(rs, pst);
closeConnection();
}
return t;
}
public static int executeScalare(String sql, Object... args) {
log.debug("BaseDao..executeScalar sql:" + sql);
try {
//pst= getConnection().prepareStatement(sql);//1.0.0
pst = createPreparedStatement(sql, args);// 1.0.1
rs = pst.executeQuery();
if (rs.next()) {
m = rs.getInt(1);
}
} catch (Exception e) {
log.debug("执行聚合函数错误。。。。" + e.getMessage());
} finally {
closeAll(rs, pst);
closeConnection();
}
return m;
}
private static <T> T convert(Class<T> clazz, ResultSet rs) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();
T t = null;
try {
t = clazz.newInstance();
for (int i = 1; i <= count; i++) {
String str = metaData.getColumnLabel(i);
Field field = clazz.getDeclaredField(str);
field.setAccessible(true);
field.set(t, rs.getObject(str));
}
} catch (InstantiationException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
log.debug("类型转换,注入属性值错误:" + e.getMessage());
}
return t;
}
/**
* ThreadLocal模式的获取数据库连接
*
* @return
*/
public static Connection getConnection() {
try {
connection = threadLocal.get();
if (connection == null || connection.isClosed()) {
connection = DriverManager.getConnection(URL, USER, PWD);
threadLocal.set(connection);
}
} catch (SQLException e) {
log.debug("创建connection 错误:" + e.getMessage());
}
return connection;
}
/**
* 释放数据库连接
*/
public static void closeConnection() {
connection = threadLocal.get();
try {
if (connection != null && !connection.isClosed()) {
connection.close();
threadLocal.remove();
}
} catch (SQLException e) {
log.debug("释放connection失败:" + e.getMessage());
}
}
/**
* 释放所有数据库连接资源
*
* @param closeables
*/
public static void closeAll(AutoCloseable... closeables) {
for (AutoCloseable closeable : closeables) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
log.debug("释放资源失败:" + e.getMessage());
}
}
}
}
/**
* 获取预编译执行对象
*
* @param sql
* @param args
* @return
*/
public static PreparedStatement createPreparedStatement(String sql, Object... args) {
log.debug("创建preparedStatement sql:" + sql);
try {
pst = getConnection().prepareStatement(sql);
if (args != null) {
for (int i = 0; i < args.length; i++) {
pst.setObject(i + 1, args[i]);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return pst;
}
/**
* 通用的增、删、改
*
* @param sql
* @param args
* @return 受影响的行数
*/
public static int executeCommand(String sql, Object... args) {
log.debug("执行增、删、改的sql:" + sql);
pst = createPreparedStatement(sql, args);
try {
m = pst.executeUpdate();
} catch (SQLException e) {
log.debug("执行增、删、改错误:" + e.getMessage());
} finally {
closeAll(pst);
closeConnection();
}
return m;
}
/**
* 开启事务
*/
public static void beginTransaction() {
Connection conn = getConnection();
if (conn != null) {
try {
conn.setAutoCommit(false);
} catch (SQLException e) {
log.debug("事务开启失败!", e);
throw new RuntimeException(e);
} finally {
threadLocal.set(conn);
}
}
}
/**
* 提交事务
*/
public static void commitTransaction() {
Connection conn = getConnection();
if (conn != null) {
try {
conn.commit();
conn.close();
} catch (SQLException e) {
log.error("commit transaction failure", e);
throw new RuntimeException(e);
} finally {
threadLocal.remove();
}
}
}
/**
* 回滚事务
*/
public static void rollbackTransaction() {
Connection conn = getConnection();
if (conn != null) {
try {
conn.rollback();
conn.close();
} catch (SQLException e) {
log.error("rollback transaction failure", e);
throw new RuntimeException(e);
} finally {
threadLocal.remove();
}
}
}
/**
* 得到执行存储过程对象
*
* @param pname
* @param args
* @return
*/
private static CallableStatement createCallableStatement(String pname, Object... args) {
try {
pname = pname.startsWith("{") ? pname : "{call " + pname + "}";
cstmt = getConnection().prepareCall(pname);
if (args != null) {
for (int i = 0; i < args.length; i++) {
cstmt.setObject(i + 1, args[i]);
}
}
} catch (SQLException e) {
log.debug("...........存储过程加载失败...........");
}
return cstmt;
}
/**
* 通用的存储过程实现增、删、改
*
* @param pname
* @param args
* @return
*/
public static int executeProcedure(String pname, Object... args) {
try {
cstmt = createCallableStatement(pname, args);
m = cstmt.executeUpdate();
} catch (Exception e) {
log.debug("。。。。。。。。。执行增、删、改的存储过程错误。。。。。。。。" + e.getMessage());
} finally {
closeAll(null, null, cstmt, getConnection());
}
return m;
}
/**
* 使用存储过程查询所有
*
* @param pname
* @param clazz
* @param args
* @return
*/
public static <T> List<T> procedureQuery(String pname, Class<T> clazz, Object... args) {
List<T> list = new ArrayList<T>(20);
T t = null;
try {
cstmt = createCallableStatement(pname, args);
rs = cstmt.executeQuery();
while (rs.next()) {
t = convert(clazz, rs);
list.add(t);
}
} catch (Exception e) {
log.debug("执行列表存储过程错误。。。。。" + e.getMessage());
} finally {
closeAll(rs, cstmt);
closeConnection();
}
return list;
}
/**
* 根据条件查询单个对象的存储过程
*
* @param pname
* @param clazz
* @param args
* @return
*/
public static <T> T procedureFindByObject(String pname, Class<T> clazz, Object... args) {
T t = null;
try {
cstmt = createCallableStatement(pname, args);
rs = cstmt.executeQuery();
if (rs.next()) {
t = convert(clazz, rs);
}
} catch (Exception e) {
log.debug("执行查询单个对象存储过程错误。。。。。" + e.getMessage());
} finally {
closeAll(rs, cstmt);
closeConnection();
}
return t;
}
}
4.4.3 通用接口实现类
package com.example.student.dao.impl;
import com.example.student.dao.IGenericDao;
import com.example.student.util.Pager;
import java.util.List;
/**
* 宋伟宁
* 2020/3/26
*/
public class GenericDao<T, PK extends Number> implements IGenericDao<T, PK> {
private Class<T> clazz;
protected GenericDao() {
//利用反射将传递T类型转换为具体的类型 Book.class User.class NewsDto.class
this.clazz = (Class<T>) ((java.lang.reflect.ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
@Override
public int save(String insert, Object... args) {
return BaseDao.executeCommand(insert, args);
}
@Override
public int delete(String delete, Integer id) {
return BaseDao.executeCommand(delete, id);
}
@Override
public int update(String udpate, Object... args) {
return BaseDao.executeCommand(udpate, args);
}
@Override
public List<T> findAll(String select, Object[] args) {
return BaseDao.findAll(select, clazz, args);
}
@Override
public List<T> findAll(String select, Object obj) {
return this.findAll(select, new Object[]{obj});
}
@Override
public List<T> findAll(String sql) {
return this.findAll(sql, null);
}
@Override
public T findObject(String select, Object[] args) {
// TODO Auto-generated method stub
return BaseDao.findByObject(select, clazz, args);
}
@Override
public T findObject(String select, Object obj) {
// TODO Auto-generated method stub
return this.findObject(select, new Object[]{obj});
}
@Override
public T findObject(String select) {
// TODO Auto-generated method stub
return this.findObject(select, null);
}
@Override
public Pager<T> pagelist(String sql, String currentPage, int pagesize, Object[] args) {
return BaseDao.pageAll(sql, clazz, currentPage, pagesize, args);
}
@Override
public Pager<T> pagelist(String sql, String currentPage, int pagesize) {
return this.pagelist(sql, currentPage, pagesize, null);
}
@Override
public Pager<T> pagelist(String sql, String currentPage, int pagesize, Object obj) {
return this.pagelist(sql, currentPage, pagesize, new Object[]{obj});
}
@Override
public int getTotal(String sql, Object[] args) {
return BaseDao.executeScalare(sql, args);
}
@Override
public int getTotal(String sql, Object object) {
// TODO Auto-generated method stub
return this.getTotal(sql, new Object[]{object});
}
@Override
public int getTotal(String sql) {
// TODO Auto-generated method stub
return this.getTotal(sql, null);
}
@Override
public int executeScalre(String sql) {
return this.executeScalre(sql, null);
}
@Override
public int executeScalre(String sql, Object object) {
return this.executeScalre(sql, new Object[]{object});
}
@Override
public int executeScalre(String sql, Object[] args) {
return BaseDao.executeScalare(sql, args);
}
}
4.5 用户实体类
@Data
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private LocalDateTime create_time;
private LocalDateTime update_time;
private Integer is_del;
}
4.6 用户DAO
public interface UserDao extends IGenericDao<User,Integer>{
String FIND_USERS="SELECT * FROM t_user";
String PAGE_USERS = "SELECT * FROM t_user ORDER BY id DESC LIMIT ?,? ";
String DELETE_USER="UPDATE t_user SET is_del=1 WHERE id=?";
String FIND_USER ="SELECT * FROM t_user WHERE id=?";
String INSERT_USER ="INSERT INTO t_user(username,password,create_time,update_time,is_del) VALUES(?,?,now(),now(),0)" ;
String UPDATE_USER = "UPDATE t_user SET username=?,password=?,update_time=now() WHERE id=?";
}
UserDaoImpl
public class UserDaoImpl extends GenericDao<User,Integer> implements UserDao {
}
4.7业务
public interface UserService {
/**
* 用户列表
* @return
*/
List<User> findAll();
}
public class UserServiceImpl implements UserService {
private UserDao userDao =new UserDaoImpl();
@Override
public List<User> findAll() {
return userDao.findAll(UserDao.FIND_USERS);
}
}
4.8 测试用户列表
@Test
public void testList(){
UserService userService =new UserServiceImpl();
userService.findAll().forEach(System.out::println);
}

4.9 用户Servlet
@WebServlet("/user")
public class UserServlet extends BaseServlet {
public String userList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<User> users = this.userService.findAll();
request.setAttribute("users",users);
return PREFIX+"user_list"+SUFFIX;
}
}
4.10 jsp页面
在WEB-INF下新增view目录,新建user_list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<html>
<head>
<title>用户列表</title>
</head>
<body>
<div>
<table style="width: 80%;border: 1px;align-content: center" border="1">
<thead>
<tr>
<th>序号</th>
<th>账号</th>
<th>密码</th>
<th>创建时间</th>
<th>是否删除</th>
</tr>
</thead>
<tbody>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.password}</td>
<td>
${user.create_time}
<%-- <fmt:formatDate value="${user.create_time}" pattern="yyyy-MM-dd HH:mm:ss"></fmt:formatDate>--%>
</td>
<td>${user.is_del}</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>

4.11 添加bootstrap样式
在WEB-INF/view下新建include.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<link rel="shortcut icon" href="#"/>
<link rel="stylesheet" href="https://cdn.staticfile.net/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.net/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.net/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
user_list.jsp 页面head部分新增如下代码:
<jsp:include page="include.jsp"/>

4.12 分页
4.12.1 新增分页业务
public class UserServiceImpl implements UserService {
private UserDao userDao =new UserDaoImpl();
@Override
public List<User> findAll() {
return userDao.findAll(UserDao.FIND_USERS);
}
@Override
public Pager<User> pageUsers(String currentPage,Integer pagesize) {
return this.userDao.pagelist(UserDao.PAGE_USERS,currentPage,pagesize);
}
}
4.12.2 Servlet
@WebServlet("/user")
public class UserServlet extends BaseServlet {
public String userList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String currentPage=request.getParameter("currentPage");
Pager<User> pager= this.userService.pageUsers(currentPage,5);
//注意:必须要设置url
pager.setUrl("user?action=userList");
request.setAttribute("pager",pager);
return PREFIX+"user_list"+SUFFIX;
}
}
4.12.3 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<html>
<head>
<title>用户列表</title>
<jsp:include page="include.jsp"/>
</head>
<body>
<div class="container">
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>序号</th>
<th>账号</th>
<th>密码</th>
<th>创建时间</th>
<th>是否删除</th>
<th>
<button class="btn btn-info">新增用户</button>
</th>
</tr>
</thead>
<tbody>
<c:forEach items="${pager.datas}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.password}</td>
<td>
${user.create_time}
<%-- <fmt:formatDate value="${user.create_time}" pattern="yyyy-MM-dd HH:mm:ss"></fmt:formatDate>--%>
</td>
<td>${user.is_del}</td>
<td>
<button class="btn btn-warning">删除</button>
<button class="btn btn-primary">修改</button>
</td>
</tr>
</c:forEach>
<tr>
<td colspan="10" align="center">
每页<select id="sel">
<c:forEach begin="5" end="20" step="5" var="i">
<option value="${i}" ${size==i ? 'selected':''}>${i}</option>
</c:forEach>
</select>条。
${pager.getItems()}
<input type="text" name="currentPage" size="2"> <input type="button" class="go" value="go">
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
五、删除用户
5.1 DAO
public interface UserDao extends IGenericDao<User,Integer>{
String DELETE_USER="UPDATE t_user SET is_del=1 WHERE id=?";
}
5.2 Service
public class UserServiceImpl implements UserService {
@Override
public boolean delete(int parseInt) {
return this.userDao.delete(UserDao.DELETE_USER,parseInt) > 0;
}
}
5.3 Servlet
public void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id=request.getParameter("id");
boolean flag=this.userService.delete(Integer.parseInt(id));
PrintWriter printWriter = response.getWriter();
printWriter.print(flag);
}
5.4 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<html>
<head>
<title>用户列表</title>
//....
<script>
$(function(){
//删除
$(".btn-warning").click(function (){
let id=$(this).val();
alert(id);
if(confirm("确认要删除编号是"+id+"的用户吗?")){
$.getJSON(
"user?action=delete",
{"id": id},
function (obj){
alert(typeof(obj));
if(obj){
alert("删除成功");
location.reload();
}else{
alert("删除失败!");
}
}
)
}
})
$(".go").click(function (){
let page=$("[name=currentPage]").val();
$.get(
location.href="user?action=userList¤tPage="+page
)
})
})
</script>
</head>
<body>
<div class="container">
<table class="table table-hover table-bordered">
<thead>
......
<button class="btn btn-warning" value="${user.id}">删除</button>
.......
</html>
六、新增
6.1 新增依赖
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.11.0</version>
</dependency>
6.2 对BeanUtils的封装
public class MyBeanUtil {
public static void copyProperties(User user, Map<String,String[]> map){
try {
BeanUtils.populate(user,map);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
6.3 DAO
public interface UserDao extends IGenericDao<User,Integer>{
String INSERT_USER ="INSERT INTO t_user(username,password,create_time,update_time,is_del) VALUES(?,?,now(),now(),0)" ;
}
6.4 Servlet
public String toAdd(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
return PREFIX+"add_user"+SUFFIX;
}
public String add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> map = request.getParameterMap();
User user=new User();
MyBeanUtil.copyProperties(user,map);
if(this.userService.add(user)) {
return "redirect:userList";
}else {
request.setAttribute("msg","新增失败");
return PREFIX+"add_user"+SUFFIX;
}
}
6.5jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增用户</title>
<jsp:include page="include.jsp"/>
<script>
$(function (){
$(".btn-success").click(function (){
$.getJSON(
"user?action=add",
$("form").serialize(),
function (obj){
alert(typeof(obj));
if(obj){
alert("新增成功!");
location="user?action=userList";
}else{
alert("新增失败!");
}
}
)
})
})
</script>
</head>
<body>
<div class="container">
<font color="red">${msg}</font>
<br>
<form>
用户账号:<input name="username" type="text"><br>
用户密码:<input type="password" name="password"><br>
密码确认:<input type="password" name="password1"><br>
</form>
<button class="btn btn-success">注册</button>
<button class="btn btn-primary">重置</button>
</div>
</body>
</html>
七、修改
7.1 修改页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改用户</title>
<jsp:include page="include.jsp"/>
<script>
$(function (){
$(".btn-success").click(function (){
$.getJSON(
"user?action=save",
$("form").serialize(),
function (obj){
if(obj){
alert("保存成功!");
location="user?action=userList";
}else{
alert("保存失败!");
}
}
)
})
})
</script>
</head>
<body>
<div class="container">
<font color="red">${msg}</font>
<br>
<form>
<input type="hidden" name="id" value="${user.id}">
用户账号:<input name="username" type="text" value="${user.username}"><br>
用户密码:<input type="text" name="password" value="${user.password}"><br>
</form>
<button class="btn btn-success">注册</button>
<button class="btn btn-primary">重置</button>
</div>
</body>
</html>
7.2 DAO
public interface UserDao extends IGenericDao<User,Integer>{
String FIND_USER ="SELECT * FROM t_user WHERE id=?";
String UPDATE_USER = "UPDATE t_user SET username=?,password=?,update_time=now() WHERE id=?";
}
7.3 业务层
@Override
public User findUserById(int id) {
return this.userDao.findObject(UserDao.FIND_USER,id);
}
@Override
public boolean update(User user) {
return this.userDao.update(UserDao.UPDATE_USER,user.getUsername(),user.getPassword(),user.getId()) > 0;
}
7.4 Servlet
public void save(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> map = request.getParameterMap();
User user=new User();
MyBeanUtil.copyProperties(user,map);
boolean flag=false;
if(Objects.isNull(user.getId())){
//如果不存在id则执行新增
flag=this.userService.add(user);
}else{
//如果id存在则还行保存
flag=this.userService.update(user);
}
PrintWriter printWriter = response.getWriter();
printWriter.print(flag);
printWriter.close();
}
public String toUpdate(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String sid = request.getParameter("id");
int id =Integer.parseInt(sid);
User user= this.userService.findUserById(id);
if(Objects.nonNull(user)){
request.setAttribute("user",user);
return PREFIX+"update_user"+SUFFIX;
}else{
request.setAttribute("msg","未查询到用户");
return PREFIX+"userList"+SUFFIX;
}
}
八、模态窗的使用
8.1 模块窗的使用
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
<link rel="shortcut icon" href="#"/>
<link rel="stylesheet" href="https://cdn.staticfile.net/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.net/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.net/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<a href="hello-servlet">Hello Servlet</a>
<h2>创建模态框(Modal)</h2>
<!-- 按钮触发模态框 -->
<button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">开始演示模态框</button>
<!-- 模态框(Modal) -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">模态框(Modal)标题</h4>
</div>
<div class="modal-body">在这里添加一些文本</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary">提交更改</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
</body>
</html>
8.2 新增和修改
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<html>
<head>
<title>用户列表</title>
<jsp:include page="include.jsp"/>
<script src="${pageContext.request.contextPath}/js/user.js"></script>
</head>
<body>
<div class="container">
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>序号</th>
<th>账号</th>
<th>密码</th>
<th>创建时间</th>
<th>是否删除</th>
<th>
<button class="btn btn-success" data-toggle="modal" data-target="#myModal">
添加
</button>
<button class="btn btn-info" onclick="location='user?action=export'">导出</button>
</th>
</tr>
</thead>
<tbody>
<c:forEach items="${pager.datas}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.password}</td>
<td>
${user.create_time}
<%-- <fmt:formatDate value="${user.create_time}" pattern="yyyy-MM-dd HH:mm:ss"></fmt:formatDate>--%>
</td>
<td>${user.is_del==0 ? '活动': '禁用'}</td>
<td>
<button class="btn btn-warning" value="${user.id}">删除</button>
<button class="btn btn-info" data-toggle="modal" data-target="#myModal${user.id}">
修改
</button>
</td>
<div class="modal fade" id="myModal${user.id}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">编辑用户</h4>
</div>
<form action="user?action=save" method="post">
<div class="modal-body">
<input type="hidden" name="id" value="${user.id}">
<%-- <input type="hidden" name="token" value="<%=System.currentTimeMillis()%>">--%>
账号:<input type="text" name="username" value="${user.username}"><br>
密码:<input type="text" name="password" value="${user.password}"><br>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button class="btn-success">提交</button>
</div>
</form>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</tr>
</c:forEach>
<tr>
<td colspan="10" align="center">
每页<select id="sel" name="pagesize">
<c:forEach begin="5" end="20" step="5" var="i">
<option value="${i}" ${pager.getPagesize() ==i ? 'selected':''}>${i}</option>
</c:forEach>
</select>条。
${pager.getItems()}
<input type="text" name="currentPage" size="2"> <input type="button" class="go" value="go">
</td>
</tr>
</tbody>
</table>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="addModalLabel">新增用户</h4>
</div>
<form action="user?action=save" method="post">
<div class="modal-body">
账号:<input type="text" name="username" value="${user.username}"><br>
密码:<input type="text" name="password" value="${user.password}"><br>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button class="btn-success">提交</button>
</div>
</form>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
</body>
</html>
8.3 修改Serlvet
package com.example.student.web.user;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.example.student.entity.User;
import com.example.student.util.MyBeanUtil;
import com.example.student.util.Pager;
import com.example.student.web.BaseServlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author TonySong
* @date 2025/8/7 0007
* @time 19:57
*/
@WebServlet("/user")
public class UserServlet extends BaseServlet {
public String userList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String size=request.getParameter("pagesize");
size = size == null ? "5": size;
Integer pagesize= Integer.parseInt(size);
String currentPage=request.getParameter("currentPage");
Pager<User> pager= this.userService.pageUsers(currentPage,pagesize);
pager.setPagesize(pagesize);
pager.setUrl("user?action=userList");
request.setAttribute("pager",pager);
return PREFIX+"user_list"+SUFFIX;
}
public void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id=request.getParameter("id");
boolean flag=this.userService.delete(Integer.parseInt(id));
PrintWriter printWriter = response.getWriter();
printWriter.print(flag);
}
public String save(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String[]> map = request.getParameterMap();
User user=new User();
MyBeanUtil.copyProperties(user,map);
boolean flag=false;
if(Objects.isNull(user.getId())){
//如果不存在id则执行新增
flag=this.userService.add(user);
}else{
//如果id存在则还行保存
flag=this.userService.update(user);
}
return flag ? "redirect:userList" : "redirect:toAdd";
}
}
九、数据导出
9.1 新增依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.4.1</version>
</dependency>
9.2 Servlet
public void export(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取所有
List<User> users = this.userService.findAll();
ExcelWriter excelWriter = ExcelUtil.getWriter();
excelWriter.write(users,true);
response.setContentType("application/vnd.ms-excel;charset=utf-8");
String fileName = null;
try {
fileName = URLEncoder.encode("员工信息", "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
response.setHeader("Content-Disposition","attachment;filename="+fileName+".xlsx");
ServletOutputStream out= null;
try {
out = response.getOutputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
excelWriter.flush(out,true);
excelWriter.close();
}
十、文件上传
10.1 Servlet
package com.example.student.web.user;
import cn.hutool.core.lang.UUID;
import com.example.student.web.BaseServlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import java.io.IOException;
/**
* @author TonySong
* @date 2025/8/11 0011
* @time 10:28
*/
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends BaseServlet {
public String upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Part part = request.getPart("pic");
String fileName = UUID.fastUUID().toString(true);
String name =part.getSubmittedFileName().substring(part.getSubmittedFileName().lastIndexOf(".")+1);
fileName = fileName+"."+name;
System.out.println(fileName);
String realPath = request.getServletContext().getRealPath("/");
System.out.println("服务器的路径是:"+realPath);
part.write(realPath+"/upload/"+fileName);
return fileName;
}
}
10.1 jsp页面
<form action="upload?action=upload" enctype="multipart/form-data" method="post">
<input type="file" name="pic">
<input type="submit" value="上传">
</form>


1万+

被折叠的 条评论
为什么被折叠?



