用JSP打造一个简单留言板:从目录结构到代码实现,深入JSP语法与Servlet交互

用JSP打造一个简单留言板:从目录结构到代码实现,深入JSP语法与Servlet交互

今天咱们来动手写一个简单的留言板,用JSP实现。功能不复杂:用户提交姓名和留言,页面展示所有留言。咱们会从目录结构讲起,配好环境,然后把代码细节讲透。特别地,这次我会重点聊聊JSP页面咋引用Servlet里的对象,还有JSP里的语法现象,顺便跟Spring Boot的思路做点对比。你用惯了Spring Boot的话,这篇能让你既摸到JSP的"老底子",又看到现代框架的影子。


目录结构:项目架子长啥样

咱们用标准的Java Web项目结构,跑在Tomcat上。目录是这样的:

bash 复制代码
message-board/
├── src/
│   └── main/
│       └── java/
│           └── com.example/
│               ├── Message.java          # 留言实体类
│               └── MessageServlet.java   # 处理请求的Servlet
├── web/
│   ├── WEB-INF/
│   │   ├── web.xml                   # 部署描述文件
│   │   └── views/
│   │       ├── index.jsp             # 主页面,表单+留言展示
│   │       └── messages.jsp          # 留言列表片段
│   └── resources/
│       └── style.css                 # 简单样式
└── pom.xml                           # Maven配置文件(可选)
  • src/main/java:Java代码放这儿,比如实体类和Servlet。
  • web/WEB-INF :核心地带,web.xml配置Servlet,views下放JSP。
  • web/resources:静态资源,比如CSS。
  • pom.xml:用Maven管依赖,不用的话手动加jar包也行。

这布局跟Spring Boot有点像,但少了自动配置,得自己动手搭。


配置环境:跑起来前的准备

JSP得跑在Servlet容器里,咱们用Tomcat,配好web.xml和依赖。

1. 配置web.xml

web.xml是项目的"指挥官",告诉Tomcat咋处理请求:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>MessageServlet</servlet-name>
        <servlet-class>com.example.MessageServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MessageServlet</servlet-name>
        <url-pattern>/message</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>WEB-INF/views/index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
  • <servlet>:注册MessageServlet,处理/message请求。
  • <welcome-file-list>:默认打开index.jsp
2. Maven依赖(可选)

用Maven的话,pom.xml加这些:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

这些是Servlet和JSP的API,Tomcat自带实现,所以用provided

3. 部署Tomcat

打成war包,丢到Tomcat的webapps目录,启动后访问http://localhost:8080/message-board/


代码实现:从实体到页面

功能简单:提交留言,展示列表。用内存存数据(实际得用数据库)。

1. 实体类:Message.java

留言的数据结构:

java 复制代码
package com.example;

public class Message {
    private String username;
    private String content;

    public Message(String username, String content) {
        this.username = username;
        this.content = content;
    }

    public String getUsername() { return username; }
    public String getContent() { return content; }
    public void setUsername(String username) { this.username = username; }
    public void setContent(String content) { this.content = content; }
}
2. Servlet:MessageServlet.java

Servlet当Controller,处理逻辑:

java 复制代码
package com.example;

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

public class MessageServlet extends HttpServlet {
    private List<Message> messages = new ArrayList<>(); // 内存存留言

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("messages", messages); // 传数据给JSP
        req.getRequestDispatcher("/WEB-INF/views/index.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String content = req.getParameter("content");

        if (username != null && content != null && !username.trim().isEmpty() && !content.trim().isEmpty()) {
            messages.add(new Message(username, content));
        }

        resp.sendRedirect(req.getContextPath() + "/message"); // 重定向刷新
    }
}
  • doGet:把messages塞进request,转发到JSP。
  • doPost:取参数,存留言,重定向避免重复提交。
3. 主页面:index.jsp

表单和留言展示:

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>留言板</title>
    <link rel="stylesheet" href="../resources/style.css">
</head>
<body>
    <h1>欢迎留言</h1>
    <form action="${pageContext.request.contextPath}/message" method="post">
        <label>姓名:</label>
        <input type="text" name="username"><br>
        <label>留言:</label>
        <textarea name="content"></textarea><br>
        <input type="submit" value="提交">
    </form>

    <h2>所有留言</h2>
    <jsp:include page="messages.jsp"/>
</body>
</html>
  • <form>:提交到/message,走POST。
  • <jsp:include>:动态引入留言列表。
4. 留言列表:messages.jsp

展示Servlet传来的数据:

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.example.Message" %>
<%@ page import="java.util.List" %>
<% List<Message> messages = (List<Message>) request.getAttribute("messages"); %>
<% if (messages != null && !messages.isEmpty()) { %>
    <ul>
        <% for (Message msg : messages) { %>
            <li><strong><%= msg.getUsername() %>:</strong> <%= msg.getContent() %></li>
        <% } %>
    </ul>
<% } else { %>
    <p>暂无留言,快来抢沙发吧!</p>
<% } %>
  • 用脚本取messages,循环展示。
5. 样式:style.css

简单美化:

css 复制代码
body { font-family: Arial, sans-serif; margin: 20px; }
h1, h2 { color: #333; }
form { margin-bottom: 20px; }
input[type="text"], textarea { width: 200px; margin: 5px 0; }
ul { list-style: none; padding: 0; }
li { padding: 10px; border-bottom: 1px solid #ddd; }

重点:JSP咋引用Servlet的对象?

Servlet和JSP的交互靠请求作用域(request scope)。具体咋弄:

  1. Servlet传对象

    • MessageServletdoGet里,用req.setAttribute("messages", messages)把留言列表塞进request。
    • forward转发到JSP时,这个对象就带过去了。
  2. JSP里取对象

    • 脚本方式messages.jsp里用<% List<Message> messages = (List<Message>) request.getAttribute("messages"); %>,强转后就能用。
    • EL表达式 :更现代点,直接${messages}取值。比如${messages[0].username}拿第一条留言的用户名。
    • 区别:脚本得手动写Java代码,EL更简洁,推荐用EL。
  3. 取值细节

    • request.getAttribute()返回Object,得强转。
    • EL自动解析对象属性,走getter方法,比如msg.getUsername()${msg.username}替代。
  4. 实际例子

    改下messages.jsp,用EL试试:

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% if (request.getAttribute("messages") != null && !((List<?>) request.getAttribute("messages")).isEmpty()) { %>
    <ul>
        ${messages.stream().map(msg -> '<li><strong>' + msg.username + ':</strong> ' + msg.content + '</li>').join('')}
    </ul>
<% } else { %>
    <p>暂无留言,快来抢沙发吧!</p>
<% } %>

但注意,JSP里EL不支持直接写循环,得用JSTL(下面会讲),这里只是展示EL取值的思路。


JSP语法现象:这些你得知道

JSP的语法挺有特色,咱们拎几个常见的讲讲:

  1. 脚本元素(<% %>)

    • <% %>:写Java代码,比如逻辑判断、循环。
    • <%= %>:输出变量值,等于out.print(),如<%= msg.getUsername() %>
    • <%! %>:声明成员变量或方法,放类级别,比如<%! int count = 0; %>
    • 现象:脚本多了页面乱,维护难,Spring Boot里Thymeleaf完全抛弃了这套。
  2. EL表达式(${})

    • 语法:${对象.属性},从request、session等作用域取值。
    • 例子:${messages.size()}取列表长度。
    • 现象:比脚本简洁,但功能有限,复杂逻辑得靠JSTL。
  3. JSTL(标签库)

    加个<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,就能用<c:forEach>循环。比如改messages.jsp

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:choose>
    <c:when test="${not empty messages}">
        <ul>
            <c:forEach var="msg" items="${messages}">
                <li><strong>${msg.username}:</strong> ${msg.content}</li>
            </c:forEach>
        </ul>
    </c:when>
    <c:otherwise>
        <p>暂无留言,快来抢沙发吧!</p>
    </c:otherwise>
</c:choose>
  • 现象 :代码干净了,跟Spring Boot里Thymeleaf的th:each很像。
  1. 隐式对象
    • JSP内置对象:requestresponsesessionapplication等。
    • 例子:request.getAttribute()取Servlet传来的值,pageContext.request.contextPath拿项目路径。
    • 现象:这些对象是Servlet环境自带的,Spring Boot里注解直接注入类似功能。

跑起来:效果咋样?

部署到Tomcat,访问http://localhost:8080/message-board/message,输入姓名和留言,提交后列表就出来了。简单直接,但功能全。

总结:JSP的实用与局限

这留言板是不是挺好上手?Servlet当Controller,JSP管展示,数据靠request传。JSP的脚本、EL、JSTL各有特色,但脚本多了乱,复用靠include硬凑,扩展性一般。换Spring Boot,注解取代Servlet,Thymeleaf干掉脚本,体验直接拉满。不过JSP这套路,是不是让你对Spring Boot的底层多了点熟悉感?从这儿走来的技术,根儿还是相通的!

相关推荐
uzong2 小时前
技术故障复盘模版
后端
GetcharZp2 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程2 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研2 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi3 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国4 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy4 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack4 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9655 小时前
pip install 已经不再安全
后端
寻月隐君5 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github