简介:这份JSP上机实习报告文档面向高校计算机相关专业学生及Java Web初学者,围绕JSP开发技术课程实验整理,可用于课程作业参考、实验复盘与期末复习。内容覆盖JSP页面中嵌入Java代码完成算术运算、JavaBean的创建与属性封装、字符串中尖括号的HTML实体转换,以及基于JavaBean接收用户名密码并判断跳转的简易登录程序,并附有心得总结,便于读者对照理解各题实现思路与代码结构。资源包共1个doc文件,约38KB,以Word文档形式完整呈现题目、代码与实验记录,适合直接查阅与整理。目前已有102人学习下载,可作为JSP入门阶段动手练习的参考材料,帮助读者熟悉动态Web项目搭建、页面数据展示与页面跳转等基础操作。
1. 从一份 JSP 上机实习报告说起:六个小题串起 JavaWeb 入门全链路
翻到这份《JSP 上机实习报告》的时候,我第一反应是:这不就是当年被<%= 1 + 1 %>支配的恐惧吗。整份文档围绕 JSP 开发技术课程的上机任务展开,从最基础的表达式输出,一路做到 JavaBean 封装、字符串转义、登录判断和页面转发,六个题目基本把 JavaWeb 入门阶段该摸的东西都摸了一遍。它适合两类人:一是刚接触 JSP、需要一份能照着敲的练习参照的在校开发者;二是想快速回顾 Servlet/JSP 时代页面渲染套路、给老项目做维护的熟手。文档本身是实习报告形态,代码片段零散、有截断,直接复制跑不起来,所以这篇笔记的重点不是复述报告,而是把它拆成能落地复现的工程骨架,顺带把里面那些容易翻车的细节讲透。
2. 项目定位与技术栈:这份报告到底在练什么
2.1 六个题目对应的能力地图
把报告里的题目按顺序捋一遍,其实是一条很清晰的学习曲线。第一题用 JSP 表达式算1+1,练的是页面里嵌 Java 代码这件事本身;中间涉及留言输入与展示,练的是表单提交和请求参数接收;第四题定义BookInfo这个 JavaBean,练的是用 POJO 封装数据;第五题写DoString工具类做<>到<>的转义,练的是 XSS 防护的雏形意识;第六题用 JavaBean 接用户名密码、判断admin/000再转发到不同页面,练的是请求转发和简单鉴权逻辑。这六块拼起来,正好覆盖了 JSP 页面渲染、JavaBean 数据承载、工具类复用、请求流转四个核心动作。
从工程视角看,这份报告的价值不在于代码多高级,而在于它把「页面 → Bean → 工具类 → 转发」这条链路完整走了一遍。很多新手学 JSP 卡住,不是因为某个语法不会,而是不知道一个请求从浏览器发出到页面返回中间经过了哪些环节。这份报告虽然写得零散,但每个题目都对应链路里的一环,拿它当骨架去补全,比看纯理论文档有效得多。
2.2 技术栈与运行环境
报告里出现的代码全部基于经典 JavaWeb 技术组合:JSP 作为视图层、JavaBean 作为数据模型、Servlet 容器负责请求分发。开发工具明确提到 Eclipse,项目类型是 Dynamic Web Project。这套组合对应的运行时环境,常见做法是 JDK 8 搭配 Tomcat 8 或 9,Eclipse 装好 Java EE 插件后直接建动态 Web 项目即可。需要说明的是,报告里的代码片段存在明显的编码声明混乱——同一个文件里出现了ISO-8859-1和utf-8两种pageEncoding,还有多处<!DOCTYPE>重复声明,这些是文档整理时的残留,实际复现时必须统一。
| 层次 | 技术选型 | 在报告中的体现 |
|---|---|---|
| 视图层 | JSP(表达式、脚本片段) | 第一题1+1输出、留言展示 |
| 数据层 | JavaBean(POJO + getter/setter) | 第四题BookInfo |
| 工具层 | 静态工具类 | 第五题DoString转义 |
| 控制层 | JSP 内嵌判断 + 请求转发 | 第六题登录判断 |
| 容器 | Tomcat(Servlet/JSP 容器) | 动态 Web 项目运行载体 |
提示:报告里
import=".*"这种写法是无效的,通配符导入必须写成import="java.util.*"这类具体包路径,直接照抄会编译报错。
2.3 目录结构该怎么搭
报告没有给出完整目录结构,但按这套技术栈的常规做法,一个能跑起来的工程目录大致如下。这个结构我一般会直接照着建,后面所有题目的代码都往里塞,避免每个题单独建项目导致依赖和配置反复折腾。
JspLab/ ├── src/ │ └── com/ │ └── lab/ │ ├── bean/ │ │ └── BookInfo.java # 第四题 JavaBean │ │ └── LoginBean.java # 第六题登录数据承载 │ └── util/ │ └── DoString.java # 第五题转义工具 ├── WebContent/ │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符 │ │ └── lib/ # 第三方 jar(本项目基本为空) │ ├── index.jsp # 第一题入口 │ ├── message.jsp # 留言输入与展示 │ ├── login.jsp # 第六题登录表单 │ ├── welcome.jsp # 登录成功页 │ └── fail.jsp # 登录失败页 └── .classpath / .project # Eclipse 工程文件src下按包名分层,bean放数据对象、util放工具类,这是最朴素也最不容易乱的分法。WebContent是 Eclipse 动态 Web 项目的默认 Web 根目录,所有能被浏览器直接访问的 JSP 都放这里,WEB-INF下的内容对外不可见,web.xml负责全局配置。把结构先定下来,后面写代码时就不会出现「这个类该放哪」的犹豫。
3. 核心题目拆解:从表达式输出到登录转发的完整实现
3.1 第一题:JSP 表达式与脚本片段的分工
报告第一题的代码被截断得比较厉害,但核心就一句<%= 1 + 1 %>。这里要分清 JSP 里两种嵌入 Java 的方式:<%= %>是表达式标签,里面的值会被求值并直接输出到响应流;<% %>是脚本片段,里面写的是语句,不会自动输出,需要配合out.print之类的方法。第一题用表达式是最省事的写法,但实际项目里更常见的是脚本片段做逻辑、表达式做输出。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>第一题:表达式计算</title> </head> <body> <h3>计算结果:</h3> <%-- 表达式标签,1+1 的结果直接输出到页面 --%> <p>1 + 1 = <%= 1 + 1 %></p> <%-- 脚本片段写法,效果相同但需要手动输出 --%> <% int result = 1 + 1; out.print("<p>脚本片段结果:" + result + "</p>"); %> </body> </html>这段代码里pageEncoding="UTF-8"和contentType里的charset=UTF-8必须同时写,前者管 JSP 源文件本身的编码,后者管响应给浏览器的编码,只写一个很容易出现中文乱码。表达式标签<%= 1 + 1 %>等价于out.print(1 + 1),区别是表达式不能带分号。脚本片段里声明的result变量作用域是当前 JSP 的_jspService方法内部,多个片段之间可以共享,但不要在里面声明与 JSP 内置对象同名的方法,否则编译期就会报冲突。
3.2 第四题:BookInfo 这个 JavaBean 的规范细节
报告里BookInfo的代码基本完整,四个String属性加标准 getter/setter。但有个细节值得说:JavaBean 的规范要求类必须是public、有无参构造、属性通过 getter/setter 访问。报告里类名写成了小写bookinfo,这在 Java 里虽然能编译,但不符合命名规范,Eclipse 里也会给警告。另外属性类型全用String存价格和库存,实际业务里价格该用BigDecimal、库存该用int,但作为教学练习用String能降低类型转换的复杂度,这点可以理解。
package com.lab.bean; import java.io.Serializable; /** * 图书信息 JavaBean * 对应报告第四题,属性按规范使用 String 类型 */ public class BookInfo implements Serializable { private static final long serialVersionUID = 1L; private String name; // 书名 private String price; // 价格 private String stock; // 库存 private String author; // 作者 // 无参构造,JavaBean 规范要求 public BookInfo() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public String getStock() { return stock; } public void setStock(String stock) { this.stock = stock; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }实现Serializable是我额外加的,报告里没有,但 JavaBean 经常要放进 session 或做序列化传输,提前加上省得后面改。serialVersionUID显式声明是为了避免序列化版本不一致导致的InvalidClassException,这个坑在 session 钝化时特别容易踩。getter/setter 里报告原文写的是= name这种省略this.的写法,实际编译会报错,必须补上this.name = name,这是文档整理时丢字符导致的,复现时务必修正。
3.3 第五题:DoString 转义工具与 XSS 防护意识
第五题要求把字符串里的<和>替换成 HTML 实体。报告里的代码用了str=("<","<")这种写法,明显是str.replace("<", "<")被截断了。这个工具类的意义在于:用户输入的内容如果直接回显到页面,里面的 HTML 标签会被浏览器解析,轻则页面结构错乱,重则脚本注入。转义是最基础的防护手段。
package com.lab.util; /** * 字符串转义工具类 * 对应报告第五题,将 HTML 特殊字符转为实体 */ public class DoString { /** * 转义字符串中的 < 和 > * @param str 原始字符串 * @return 转义后的字符串,入参为 null 时返回空串 */ public static String change(String str) { if (str == null) { return ""; } // 先转义 & 再转义尖括号,顺序不能反 str = str.replace("&", "&"); str = str.replace("<", "<"); str = str.replace(">", ">"); return str; } }这里有个顺序问题:必须先替换&,再替换<和>。如果先替换尖括号,<里的&会在后续替换中被二次转义成&lt;,页面就会显示成字面的<而不是<。报告原文只处理了尖括号,没处理&,严格说是不完整的。另外replace方法接收的是CharSequence,传字符串字面量没问题,但如果入参是null会抛NullPointerException,所以开头加了空值判断。这个工具类方法声明为static,调用时直接DoString.change(...),不需要实例化,这也是工具类的常规写法。
3.4 第六题:登录判断与请求转发的两种路径
第六题是整份报告里逻辑最完整的一题:用 JavaBean 接用户名密码,判断是否为admin/000,是则转发到欢迎页,否则转发到失败页。报告里用的是 JSP 脚本片段里直接写if-else加request.getRequestDispatcher(...).forward(...)。这里要区分转发(forward)和重定向(redirect):转发是服务器内部跳转,浏览器地址栏不变,请求域里的数据能带过去;重定向是让浏览器重新发一个请求,地址栏会变,请求域数据丢失。登录场景用转发更合适,因为要把用户信息带到目标页。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.lab.bean.LoginBean" %> <% // 接收表单参数,统一按 UTF-8 处理 request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); // 用 JavaBean 承载登录数据 LoginBean login = new LoginBean(); login.setUsername(username); login.setPassword(password); // 判断逻辑,实际项目应查数据库而非硬编码 if ("admin".equals(login.getUsername()) && "000".equals(login.getPassword())) { request.setAttribute("loginUser", login); request.getRequestDispatcher("/welcome.jsp").forward(request, response); } else { request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("/fail.jsp").forward(request, response); } %>request.setCharacterEncoding("UTF-8")必须放在读取任何参数之前,否则对 POST 请求体无效。判断时把常量"admin"写在equals前面,是为了避免username为null时抛空指针,这是字符串比较的惯用写法。forward之后当前 JSP 的后续代码不会再执行,但严格说forward之后应该return,防止容器在某些实现下继续往下走。request.setAttribute存进去的对象在目标页用request.getAttribute取,作用域仅限当前请求,这也是转发能带数据、重定向带不了的根本原因。
4. 避坑与排查:复现这份报告时最容易翻车的五处
4.1 中文乱码:编码声明三处必须对齐
现象是页面上的中文显示成问号或乱码。原因通常是 JSP 源文件编码、pageEncoding、响应charset三者不一致,报告里ISO-8859-1和utf-8混用就是典型。解决方式是把 JSP 文件本身存为 UTF-8,page指令里pageEncoding="UTF-8"和contentType="text/html; charset=UTF-8"同时写,表单提交的 POST 请求在接收参数前加request.setCharacterEncoding("UTF-8")。三处对齐后,乱码基本不会再出现。
4.2 类找不到:包名与 import 必须匹配
现象是 JSP 里new BookInfo()报BookInfo cannot be resolved to a type。原因是 JavaBean 放在com.lab.bean包下,JSP 里没写<%@ page import="com.lab.bean.BookInfo" %>,或者类文件没编译到WEB-INF/classes下。解决方式是检查 import 指令的包路径是否和package声明一致,Eclipse 里确认 Build Path 输出目录指向WebContent/WEB-INF/classes,改完类后重新 Clean 再部署。
4.3 转发后 404:路径前导斜杠不能丢
现象是登录判断走对了分支,但页面报 404。原因是getRequestDispatcher("welcome.jsp")少了前导/,容器会按相对当前请求路径去解析,结果找不到。解决方式是统一写成getRequestDispatcher("/welcome.jsp"),以 Web 根为基准。这个斜杠我见过太多人漏,尤其是从相对路径思维切过来的时候。
4.4 转义顺序错:& 被二次转义
现象是用户输入<b>转义后页面显示<b>而不是预期的<b>文本。原因是先替换了尖括号再替换&,导致实体里的&又被转一次。解决方式是严格按&→<→>的顺序替换,或者直接用成熟的转义工具。这个顺序问题在自写工具类时特别隐蔽,测试时拿一个带&的字符串就能验出来。
4.5 Tomcat 版本与 JDK 不匹配:启动即报错
现象是项目部署后 Tomcat 启动失败,日志里出现UnsupportedClassVersionError。原因是编译用的 JDK 版本高于 Tomcat 运行时的 JDK 版本,class 文件版本号不被识别。解决方式是统一 JDK 版本,Eclipse 里检查项目的 Compiler compliance level 和 Tomcat 配置的 JRE 是否一致,常见做法是都锁在 JDK 8。这个坑在换机器或换环境时高频出现,血泪经验是部署前先看一眼两边的版本号。
5. 二次开发与改造思路:把练习代码变成可维护的骨架
5.1 从 JSP 脚本片段迁移到 Servlet + Bean
报告里所有逻辑都写在 JSP 里,这在教学阶段没问题,但真实项目里 JSP 只该负责展示,业务逻辑应该挪到 Servlet 或 Controller 里。改造的第一步是把第六题的登录判断从 JSP 抽到一个LoginServlet,JSP 只保留表单和结果展示。这样做的直接好处是逻辑可测试、可复用,页面里不再出现大段 Java 代码。迁移时注意web.xml里要配 Servlet 映射,或者用注解@WebServlet("/login"),后者更省事。
package com.lab.servlet; import com.lab.bean.LoginBean; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); LoginBean login = new LoginBean(); login.setUsername(req.getParameter("username")); login.setPassword(req.getParameter("password")); if ("admin".equals(login.getUsername()) && "000".equals(login.getPassword())) { req.setAttribute("loginUser", login); req.getRequestDispatcher("/welcome.jsp").forward(req, resp); } else { req.setAttribute("errorMsg", "用户名或密码错误"); req.getRequestDispatcher("/fail.jsp").forward(req, resp); } } }@WebServlet("/login")把 URL 映射到 Servlet,表单的action改成login即可。doPost里处理登录是因为表单用 POST 提交,如果表单是 GET 则要重写doGet。改造后 JSP 里的脚本片段全部删掉,页面只留 HTML 和少量 EL 表达式,可读性提升明显。
5.2 用 JSTL 和 EL 替换脚本片段
JSP 里<% %>写多了维护成本很高,改造方向是用 JSTL 标签库配合 EL 表达式。比如留言展示从out.print改成<c:forEach>遍历,登录用户信息用${loginUser.username}取。需要引入jstl.jar和standard.jar到WEB-INF/lib,页面顶部加<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>。EL 表达式默认会处理null,取不到值时输出空串而不是报错,这点比脚本片段友好。改造时注意 EL 只能访问 JavaBean 的 getter,属性名首字母小写,getUsername对应${loginUser.username}。
5.3 验证改造是否成功的检查清单
改完之后怎么确认没改坏,我一般按这个顺序过一遍:先看登录成功和失败两条路径是否都能正确转发,再看中文是否正常显示,然后拿带<script>的输入测转义是否生效,最后检查 session 里有没有残留不该存的对象。下面这个表格是我常用的验证对照,改一处勾一处,比凭感觉靠谱。
| 验证项 | 操作 | 预期结果 |
|---|---|---|
| 登录成功 | 输入 admin / 000 | 转发到 welcome.jsp,显示欢迎信息 |
| 登录失败 | 输入 admin / 123 | 转发到 fail.jsp,显示错误提示 |
| 中文显示 | 页面含中文标题 | 无乱码 |
| 转义生效 | 留言输入<b>test</b> | 页面显示字面文本,不加粗 |
| 空值处理 | 不填用户名直接提交 | 不抛空指针,走失败分支 |
这套检查清单不复杂,但每次改完都走一遍,能挡掉大部分低级问题。从那以后我每次动 JSP 老项目,都强制先把编码、import、转发路径这三样过一遍再谈功能,省下来的排查时间远比检查花的多。希望这份拆解能帮到你,把这份报告里的六个小题真正跑成自己的东西。
本文还有配套的精品资源,点击获取