用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的底层多了点熟悉感?从这儿走来的技术,根儿还是相通的!

相关推荐
绝无仅有6 分钟前
Docker Compose 安装Elasticsearch8和kibana和mysql8和redis5 并重置密码的经验与总结
后端·面试·架构
程序员皮蛋鸽鸽23 分钟前
从零配置 Linux 与 Windows 互通的开发环境
前端·后端
异常君25 分钟前
揭秘 Spring 验证机制:为何@Validated 在 Controller 层生效却在 Service 层失效?
java·后端·spring
Yharim27 分钟前
ruoyi-cloud分页是怎么回事
java·后端·微服务
我是谁的程序员32 分钟前
Flutter中极光推送的使用----jpush_flutter
后端
小吕学编程37 分钟前
基于Canal+Spring Boot+Kafka的MySQL数据变更实时监听实战指南
数据库·后端·mysql·spring·kafka
几颗流星38 分钟前
Rust中的闭包
后端·rust
[email protected]40 分钟前
ASP.NET Core Web API 配置系统集成
后端·asp.net·.netcore
追逐时光者1 小时前
一个开源的 Blazor 跨平台入门级实战项目
后端·.net