Servlet 3.0 + JSP 用户管理 CRUD 实战:从零构建企业级开发思维

在Java Web开发领域,掌握Servlet和JSP技术栈是每位开发者的必修课。本文将带您从实战角度出发,通过一个完整的用户管理模块开发过程,深入理解MVC架构的精髓。不同于简单的代码堆砌,我们将重点关注模块化设计、数据流向控制和可维护性实践。

1. 项目架构设计与环境准备

1.1 技术选型与项目结构

现代Java Web开发虽然涌现了Spring Boot等框架,但理解原生Servlet/JSP的工作机制仍是核心基础。我们的技术栈选择:

  • Servlet 3.0 :支持注解配置,简化web.xml管理
  • JSP 2.2 :结合EL表达式和JSTL标签库
  • MySQL 5.7+ :关系型数据库标准选择
  • Maven :项目依赖管理工具

标准项目目录结构应遵循Maven约定:

src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           ├── model/      # 数据模型
│   │           ├── dao/        # 数据访问层  
│   │           ├── service/    # 业务逻辑层
│   │           └── web/        # 控制器层
│   ├── resources/              # 配置文件
│   └── webapp/
│       ├── WEB-INF/
│       │   └── views/          # JSP视图文件
│       └── static/             # 静态资源

1.2 数据库设计与初始化

用户表设计应考虑基础字段和扩展性:

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  `email` varchar(100) DEFAULT NULL,
  `phone` varchar(20) DEFAULT NULL,
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `status` tinyint(1) DEFAULT '1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

提示:密码字段应存储加密后的值,推荐使用BCrypt等强哈希算法

2. 核心组件实现

2.1 领域模型构建

采用Java Bean规范定义用户实体:

package com.example.model;

import java.time.LocalDateTime;

public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
    private String phone;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private Boolean status;
    
    // 省略getter/setter和构造方法
}

2.2 数据访问层实现

DAO层采用JDBC模板模式,避免重复代码:

public class UserDao {
    private final DataSource dataSource;
    
    public UserDao(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    public User findById(int id) throws SQLException {
        String sql = "SELECT * FROM users WHERE id = ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setInt(1, id);
            ResultSet rs = stmt.executeQuery();
            if (rs.next()) {
                return mapUser(rs);
            }
            return null;
        }
    }
    
    private User mapUser(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getInt("id"));
        user.setUsername(rs.getString("username"));
        // 其他字段映射...
        return user;
    }
}

2.3 服务层设计

服务层处理业务逻辑和事务管理:

public class UserService {
    private final UserDao userDao;
    
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public User getUserById(int id) throws BusinessException {
        try {
            User user = userDao.findById(id);
            if (user == null) {
                throw new BusinessException("用户不存在");
            }
            return user;
        } catch (SQLException e) {
            throw new BusinessException("数据库查询失败", e);
        }
    }
}

3. 控制器与视图开发

3.1 Servlet控制器实现

使用Servlet 3.0注解简化配置:

@WebServlet("/users/*")
public class UserServlet extends HttpServlet {
    private UserService userService;
    
    @Override
    public void init() throws ServletException {
        DataSource dataSource = (DataSource) getServletContext()
            .getAttribute("dataSource");
        userService = new UserService(new UserDao(dataSource));
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException {
        
        String pathInfo = req.getPathInfo();
        if (pathInfo == null || pathInfo.equals("/")) {
            listUsers(req, resp);
        } else if (pathInfo.equals("/new")) {
            showCreateForm(req, resp);
        } else {
            String[] parts = pathInfo.split("/");
            if (parts.length == 2) {
                showUser(Integer.parseInt(parts[1]), req, resp);
            }
        }
    }
    
    private void listUsers(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 获取用户列表逻辑
        req.getRequestDispatcher("/WEB-INF/views/users/list.jsp")
           .forward(req, resp);
    }
}

3.2 JSP视图开发

采用JSTL和EL表达式简化页面逻辑:

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <title>用户列表</title>
</head>
<body>
    <h2>用户管理</h2>
    <table>
        <thead>
            <tr>
                <th>ID</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.email}</td>
                    <td>
                        <a href="<c:url value='/users/${user.id}'/>">查看</a>
                    </td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
</body>
</html>

4. 完整CRUD流程实现

4.1 创建用户流程

表单提交到Servlet的POST处理:

@WebServlet("/users")
public class UserServlet extends HttpServlet {
    // ...其他方法
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        
        User user = new User();
        user.setUsername(req.getParameter("username"));
        user.setPassword(encrypt(req.getParameter("password")));
        // 其他字段处理...
        
        try {
            userService.createUser(user);
            resp.sendRedirect(req.getContextPath() + "/users");
        } catch (BusinessException e) {
            req.setAttribute("error", e.getMessage());
            req.getRequestDispatcher("/WEB-INF/views/users/new.jsp")
               .forward(req, resp);
        }
    }
}

4.2 更新与删除实现

采用RESTful风格设计URL:

@WebServlet("/users/*")
public class UserServlet extends HttpServlet {
    // ...其他方法
    
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        
        // 解析ID
        String[] parts = req.getPathInfo().split("/");
        int userId = Integer.parseInt(parts[1]);
        
        // 更新逻辑...
    }
    
    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        
        // 解析ID并删除
        String[] parts = req.getPathInfo().split("/");
        int userId = Integer.parseInt(parts[1]);
        userService.deleteUser(userId);
        resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
    }
}

5. 高级特性与优化

5.1 分页查询实现

服务层添加分页支持:

public class UserService {
    public Page<User> listUsers(int page, int size) {
        // 计算偏移量
        int offset = (page - 1) * size;
        List<User> users = userDao.findByPage(offset, size);
        int total = userDao.count();
        return new Page<>(users, page, size, total);
    }
}

5.2 表单验证与错误处理

前端结合后端验证:

<c:if test="${not empty errors}">
    <div class="alert alert-danger">
        <ul>
            <c:forEach items="${errors}" var="error">
                <li>${error}</li>
            </c:forEach>
        </ul>
    </div>
</c:if>

5.3 日志与异常处理

配置全局异常处理器:

@WebServlet("/errorHandler")
public class ErrorHandlerServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        
        Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
        logger.error("请求处理异常", throwable);
        
        // 根据异常类型返回不同错误页面
        if (throwable instanceof BusinessException) {
            req.getRequestDispatcher("/WEB-INF/views/error/business.jsp")
               .forward(req, resp);
        } else {
            req.getRequestDispatcher("/WEB-INF/views/error/system.jsp")
               .forward(req, resp);
        }
    }
}

6. 项目部署与测试

6.1 Tomcat配置优化

server.xml连接池配置:

<Context>
    <Resource name="jdbc/UserDB" 
              auth="Container"
              type="javax.sql.DataSource"
              maxTotal="100"
              maxIdle="30"
              maxWaitMillis="10000"
              username="dbuser"
              password="dbpass"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/userdb"/>
</Context>

6.2 集成测试策略

使用HttpClient进行端到端测试:

public class UserSystemTest {
    @Test
    public void testCreateUser() throws Exception {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost("http://localhost:8080/users");
        
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("username", "testuser"));
        // 其他参数...
        
        post.setEntity(new UrlEncodedFormEntity(params));
        try (CloseableHttpResponse response = client.execute(post)) {
            assertEquals(302, response.getStatusLine().getStatusCode());
            // 验证重定向位置
        }
    }
}

通过这个完整的开发流程,您不仅掌握了Servlet/JSP的技术实现,更重要的是理解了企业级Web应用的分层架构和最佳实践。这种模块化、分层的设计思想,正是现代Java Web框架(如Spring MVC)的基础。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐