☰
JSP上机六题实战:page指令、JavaBean装配与页面跳转
2026/11/3 22:05:39 网站建设 项目流程

简介:这份资源是一份JSP开发技术课程的上机实习报告文档,面向正在学习Java Web与JSP基础的高校学生及自学者,可用于对照完成课堂实验、复习知识点或整理课程作业。报告围绕JSP页面开发的核心技能展开,包含在JSP中嵌入Java代码完成算术运算并输出页面结果、创建BookInfo等JavaBean封装属性、编写工具类对字符串中的尖括号进行HTML实体转义、以及基于JavaBean接收用户名与密码并实现登录判断和页面转发等实训题目,每道题均附有题目说明与对应代码片段,末尾还记录了环境搭建、页面跳转与调试排错的学习心得。资源包内共1个doc文档,压缩包约38KB,体积小巧、结构完整,便于直接查阅与打印。目前已有102人学习浏览,适合作为JSP上机课的参考范例与期末复习材料。

1. JSP 上机六题的知识地图:一条主线串起脚本、Bean 与页面跳转

把六道题摊开看,很多人第一反应是六个互不相干的小练习:算个 1+1、做个留言板、写个 BookInfo、写个 DoString、再做个登录判断。梳理下来会发现它们其实站在同一条链路上——页面怎么执行 Java 脚本、数据怎么装进 JavaBean、Bean 怎么在页面之间传递、输出之前怎么转义。手里那份 JSP 上机实习报告文档资料之所以看着乱,往往不是代码行数少,而是这条线没被抽出来,六个题各自为战。

这条链路适合两类人:正在赶课程上机的,以及隔了半年要回头补 JavaBean 与页面跳转的。下面就按运行底座、数据传递、工具封装、易错点、复习清单的顺序重排一遍,把它变成一张能照着走的地图。

2. 运行底座:动态 Web 项目、page 指令与三种脚本元素

2.1 动态 Web 项目在 IDE 里的最小骨架

动态 Web 项目在 IDE 里看着是一堆图标和节点,落到磁盘上其实就是一份符合目录约定的文件夹。服务器认的是这套约定,不是新建向导点了多少下。新建时选 Dynamic Web Project,目标运行时指向本机的 Tomcat,内容目录保持默认,勾上自动生成 web.xml,骨架就成型了。真正决定页面跑不跑得起来的,往往不是代码写得多漂亮,而是文件有没有放对位置。

位置职责放错后的典型症状
Java 源目录(src)放包和普通 Java 类,编译产物进 WEB-INF/classesBean 类找不到,抛 ClassNotFoundException
内容根目录(WebContent / webapp)放 jsp、html、css、js,可以被浏览器直接访问访问 404,路径对不上
WEB-INF/web.xml部署描述符,配欢迎页、Servlet 映射、过滤器首页打不开,映射不生效
WEB-INF/lib第三方 jar 包页面编译期报找不到符号
WEB-INF 下的 jsp只能由服务器内部转发进入直接在地址栏敲路径一律 404

访问路径的构成是「上下文路径 + 页面相对路径」,上下文路径默认等于项目名,端口默认 8080。刚部署完打不开页面,先按顺序查三件事:项目是否真的发布到了服务器上、路径大小写是否一致、文件是否落在内容根目录里而不是放在项目根目录外面。改 JSP 一般保存即生效,改 Java 类则要重新发布甚至重启服务器——这条血泪经验我在第一次改 Bean 改了半小时没反应之后才记住。另外 WEB-INF 这个目录值得单独说一句:它不是权限噱头,而是容器层面的约定,浏览器发起的请求根本进不去,所以任何需要登录后才能看的页面,放进去比写判断更省事。

2.2 page 指令的六个高频属性与编码链条

page 指令写在 JSP 最上面,语法是<%@ page 属性="值" %>,它不产生任何输出,只告诉容器怎么翻译这个文件。上机题里出错率最高的部分,几乎都集中在这一行。属性看起来多,真正天天用到的就六个。

属性作用常见取值踩坑点
language指定脚本语言java一般不用改
contentType响应头的 Content-Typetext/html; charset=UTF-8少了 charset,浏览器按默认编码猜
pageEncoding容器按什么编码读这个 jsp 源文件UTF-8写成 ISO-8859-1,中文直接乱码
import导入 Java 包java.util.List不能写.*,必须写完整包名
session是否自动创建 sessiontrue / false纯展示页设 false 可省内存
errorPage出错后跳转到哪个页面/error.jsp目标页要配 isErrorPage="true"

编码这件事要拆成两段看:pageEncoding 管的是「容器读源文件」这一段,contentType 里的 charset 管的是「响应写给浏览器」这一段。两段用不同编码,结果就是源文件里的中文在编译期就已经坏了,后面怎么调都没用。实习报告里出现 pageEncoding="ISO-8859-1" 的写法,页面上的中文必然是问号或方块,这不是玄学,是链条断在了第一环。稳妥的做法是两处都写 UTF-8,并且确认编辑器保存文件时用的也是 UTF-8。

import 属性的另一个坑是通配写法。import=".*"编译不过,因为通配符只能跟在包名后面,比如java.util.*。JSP 本身已经默认导入了 java.lang.、javax.servlet.、javax.servlet.jsp.、javax.servlet.http.这四个包,所以 request、response、out 这些内置对象不用导入就能用,很多人误以为是语法特例。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import="java.util.List, java.util.ArrayList" %> <%-- import 是 page 指令里唯一允许重复出现的属性,写两行等价于逗号分隔 --%>

上面两行分别锁死了响应编码和源文件编码,并用逗号分隔的方式导入了两个集合类。如果只导一个包,直接写import="java.util.List"即可;写成java.util.*也能过,但正式项目里更推荐列全,避免类名冲突。

2.3 声明、脚本片段、表达式:三种脚本元素的分工

JSP 里的 Java 代码有三种写法,位置和作用完全不同,混用是编译错误的头号来源。理解它们的关键是知道 JSP 最终会被翻译成一个 Servlet:声明里的内容变成这个 Servlet 的成员,脚本片段变成 service 方法的方法体,表达式变成一句 out.print。

语法翻译后落在哪典型用途注意
<%! %>Servlet 类的成员位置定义方法、静态变量多线程共享,慎用可变状态
<% %>_jspService 方法体内写逻辑、循环、判断里面不能定义方法
<%= %>等价于 out.print(...)输出变量或表达式结果结尾不能加分号
<%-- --%>不输出,不发送到浏览器注释和 HTML 注释不是一回事

那道算 1+1 的题,最短写法一行就够:<%= 1 + 1 %>。想看清执行过程,可以拆成脚本片段加表达式两步。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <% // 脚本片段:这些语句会原样进入 _jspService 方法体,按顺序执行 int a = 1; int b = 1; int sum = a + b; %> <!DOCTYPE html> <html> <head><meta charset="UTF-8"><title>求和</title></head> <body> <p>脚本片段输出:<% out.print(sum); %></p> <p>表达式输出:<%= sum %></p> <%-- 表达式标签后面写分号会变成 out.print(sum;),直接编译报错 --%> </body> </html>

int a、int b、int sum三个局部变量只在这一次请求内有效,多个用户同时访问互不影响;如果把它们写进<%! %>声明里,就变成了实例成员,并发访问时会出现互相覆盖的问题,这是后面学 Servlet 线程安全时的伏笔。out是九个内置对象之一,类型是 JspWriter,带缓冲区,表达式标签本质就是调用它的 print。写完这段可以对照翻译后的 Java 文件看一眼,位置关系会立刻清楚,比背结论管用。

3. 数据穿页面:JavaBean 规范、属性读写与登录题的转发链路

3.1 一个合格 JavaBean 的四条硬条件

JavaBean 不是某种特殊语法,而是一套命名约定,容器和标签靠反射按约定找属性和方法。约定不满足,页面不会报「你写错了」,而是静默地取不到值,排查起来格外费劲。判定标准就四条。

条件具体要求不满足的后果
类是 public且所在包能被页面 import 到页面无法实例化
有无参构造public 且无参数标签创建实例失败
属性私有字段用 private 修饰违背封装,标签也不认
提供访问器getXxx / setXxx,boolean 用 isXxx属性名推导不出来,取值为空

属性名不是字段名,而是从访问器反推出来的:有 getName() 就有名为 name 的属性,有 isVip() 就有名为 vip 的属性。这一点在写<jsp:setProperty property="*">时特别关键,表单控件的 name 必须和推导出来的属性名逐字一致,大小写都不能差。

BookInfo 这道题要求四个 String 属性,代码骨架如下。

package hbe; import java.io.Serializable; public class BookInfo implements Serializable { // 四个属性全部 private,外部只能走访问器 private String name; private String price; private String stock; private String author; // 显式保留无参构造:一旦写了带参构造,编译器就不再送默认构造 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; } }

this.name = name这句不能省,因为参数名和字段同名,不加 this 就是把参数赋给自己,字段永远是 null,页面显示空白却没有任何报错。实现 Serializable 是可选动作,但放进 session 作用域时容器可能要求可序列化,先加上不吃亏。

这里有个值得想一层的地方:题目把 price 和 stock 也定义成 String,照做没问题,但真实场景里价格用字符串会翻车——"10" 和 "9" 按字符串比大小,结果是 "10" 更小。正经设计会用 BigDecimal 存金额、用 int 存库存,转换放在 Bean 内部做。上机按题目要求写,心里要留这根弦。

3.2 登录题:从表单参数到 Bean 属性的完整链路

登录题考的不是 if 判断,而是「参数怎么进 Bean、结果怎么送到下一个页面」这条链路。完整结构是三个页面加一个 Bean:表单页负责收集,处理页负责装配和判断,结果页负责展示。表单用 post 提交,控件名与 Bean 属性同名。

<%-- 处理页:doLogin.jsp --%> <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <% // 必须在任何 getParameter 之前设置,否则 POST 上来的中文已经按默认编码解完了 request.setCharacterEncoding("UTF-8"); %> <jsp:useBean id="user" class="hbe.LoginBean" scope="request" /> <jsp:setProperty name="user" property="*" /> <% // 常量写在前面,避免 username 为 null 时抛空指针 if ("admin".equals(user.getUsername()) && "000".equals(user.getPassword())) { request.getRequestDispatcher("/welcome.jsp").forward(request, response); } else { request.getRequestDispatcher("/fail.jsp").forward(request, response); } return; // 转发之后立刻收手,防止后续输出干扰响应 %>

property="*"的含义是:把本次请求里所有与 Bean 属性同名、类型兼容的参数自动写进去,不用一个个 getParameter 再 set。它依赖反射,所以属性名拼错不会报错,只会静默地留 null。scope="request"表示这个 Bean 只在当前请求内有效,转发到 welcome.jsp 后仍然能取到;如果选 page,转发过去就没了。

判断语句里把常量"admin"放在前面,是为了在用户名为 null 时返回 false 而不是抛异常,这条习惯在真实项目里能省掉大量空指针排查。最后那句 return 也值得单拎出来:转发只是把请求交给另一个资源,当前页面剩下的代码照样会执行,不 return 就可能出现响应已提交、再写内容报错的尴尬。

3.3 转发与重定向:两条跳转路径的区别

登录成功用转发、失败回登录页用重定向,这个搭配不是随意定的,背后是两条完全不同的路径。

维度forward(转发)sendRedirect(重定向)
请求次数一次两次
地址栏不变变成新地址
request 作用域保留,Bean 还能取到丢失,数据要重新准备
目标位置服务器内部资源,可进 WEB-INF浏览器重新发起,进不去 WEB-INF
写法request.getRequestDispatcher("/x.jsp").forward(request, response)response.sendRedirect(request.getContextPath() + "/x.jsp")

转发适合「同一批数据换个页面展示」,比如登录成功把用户对象带进主页;重定向适合「状态已经变了,让浏览器重新请求」,比如提交表单成功后跳回列表页,顺手规避刷新重复提交。目标路径的写法也容易错:转发里的/是相对于上下文根,重定向里的/是相对于域名根,所以重定向一般要拼上request.getContextPath(),否则部署在非根路径下就会 404。

4. 工具类 Bean 与转义:DoString 到底解决了什么问题

4.1 实体 Bean 与工具类 Bean 的定位差别

同样是「JavaBean」,DoString 和 BookInfo 完全不是一类东西。BookInfo 有状态,装的是某本书的数据,每个实例各不相同;DoString 没有状态,只有一组转换方法,谁调用结果都一样。把两者分清,才知道什么时候该用<jsp:useBean>,什么时候直接调静态方法。

对比项实体 Bean(BookInfo)工具类 Bean(DoString)
是否有状态有,靠私有字段保存数据无,只做计算
方法类型实例方法通常声明为 static
页面用法useBean 创建实例 + setProperty 装配直接 类名.方法名() 调用
作用域需要绑定 page/request/session不涉及作用域
典型场景表单数据承载、数据库行映射字符串处理、日期格式化、编码转换

严格按规范讲,工具类其实不完全符合 JavaBean 定义,因为它没有可读写的属性;课程里把「被 JSP 调用的普通 Java 类」统称为 JavaBean,属于约定俗成的宽松说法。用的时候心里要清楚:静态方法不需要实例,页面里写<jsp:useBean>反而是多余动作。

4.2 转义的本质与替换顺序的坑

留言、评论这类用户输入最终会出现在 HTML 里,如果里面带了尖括号,浏览器会把它当标签解析,轻则布局错乱,重则脚本被执行。转义的思路很朴素:把有特殊含义的字符换成对应的 HTML 实体,让浏览器把它当普通文字显示。

原字符实体不转义的后果
&&后续转义结果被二次解析
<<被当标签开始
>>标签结构被破坏
""属性值被截断
package hby; public class DoString { // 声明为 static,页面里用 DoString.escapeHtml(...) 直接调,不必 new public static String escapeHtml(String str) { if (str == null) { return ""; } // 顺序不能反:& 必须最先替换,否则后生成的 &lt; 会被再转义成 &amp;lt; return str.replace("&", "&amp;") .replace("<", "&lt;") .replace(">", "&gt;") .replace("\"", "&quot;"); } }

这里用的是 String.replace(CharSequence, CharSequence),做的是字面量全量替换,不涉及正则;如果误用 replaceAll,第一个参数会被当正则解析,<这类字符虽然大多不触发问题,但遇到|、.就会莫名其妙替换错误。替换顺序是最容易被忽略的一点:先换尖括号再换 &,原本的<变成了&lt;,紧接着 & 那一步又把它变成&amp;lt;,页面上就会原样显示&lt;这串字符,看起来像「转义没生效」,实际是转义了两遍。

4.3 留言板:输入与展示两端的分工

留言板这类页面通常两种动作合在一处:提交新留言、展示已有留言。存储介质在上机环境里用 application 或 session 作用域里的 List 就够,不必上数据库。关键决策是转义放在哪一端——存的时候转,还是展示的时候转。

推荐存原始内容、展示时转义。理由很实际:原始数据只有一份,将来要导出、要按关键字搜索、要换个端展示,都不会被实体字符干扰;而展示端是唯一会把内容拼进 HTML 的地方,在那里转义,责任边界最清楚。

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import="java.util.List, java.util.ArrayList, hby.DoString" %> <% // application 作用域:整个应用共享一份,服务器重启才清空 List<String> messages = (List<String>) application.getAttribute("messages"); if (messages == null) { messages = new ArrayList<String>(); application.setAttribute("messages", messages); } request.setCharacterEncoding("UTF-8"); String content = request.getParameter("content"); if (content != null && content.trim().length() > 0) { messages.add(content); // 存原文,转义留到输出时做 } %>

第一次访问时 List 还不存在,所以要先判空再创建并放回 application,否则每次请求都是新的空列表,留言永远只有一条。判空之后再取参数、去空格、判断非空,避免把一堆空行塞进去。展示时逐个用<%= DoString.escapeHtml(msg) %>输出,双花括号里的表达式会把返回值直接写进 HTML,尖括号在这时候才被换成实体。

5. 易错点排查:JSP 上机最容易翻车的五处细节

5.1 编码只改了一半

最容易漏的是 pageEncoding 与 contentType 里的 charset 只改了一个。漏的原因很直接:这两个属性长得像,写在一行里,改完页面能打开,就以为没问题了,直到某段中文变成问号才发现。补的办法是把这一行当成模板固定下来,两处都写 UTF-8,并且确认编辑器保存编码一致;再遇到乱码,先看响应头里的 Content-Type 有没有 charset,再看源文件是不是真按 UTF-8 存的,两处都排除之后再怀疑数据库连接串。

5.2 设编码的时机晚了一步

第二个高频坑是request.setCharacterEncoding("UTF-8")写在取参数之后。漏的原因是代码顺序看起来无所谓,反正都在一个页面里。实际上这行必须在第一次调用 getParameter 之前执行,一旦参数被解析过,再设就无效了。更隐蔽的是<jsp:setProperty property="*">内部就会触发参数读取,所以它必须排在设编码之后。补的做法是把设编码放在页面最顶部的脚本片段里,或者干脆写一个过滤器统一处理,一劳永逸。

5.3 表达式标签后面多写一个分号

<%= sum; %>是新手最常见的编译错误。漏的原因是写惯了 Java 语句,觉得分号是标配。表达式标签翻译后是out.print(sum;),语法直接不成立。补的办法是记住一句口诀:<% %>里写完整语句带分号,<%= %>里只放一个表达式不带分号。同理,表达式里也不能写 if、for 这类语句,需要逻辑就用脚本片段包起来。

5.4 转发之后没有收手

forward 写完继续往下执行,这个问题在页面上不一定报错,但会产生多余输出甚至响应已提交异常。漏的原因是转发看起来像 return,其实只是一次方法调用。补的办法是在 forward 或 sendRedirect 之后立刻加 return;另外要记住重定向进不去 WEB-INF,需要保护的页面只能走转发。

5.5 包名与 import 对不上

Bean 明明写了,页面却报 ClassNotFoundException,八成是包名和 import 不一致,或者类没编译到 WEB-INF/classes 下。漏的原因是 IDE 会自动补 import,手写时容易写成import=".*"这种非法通配。补的办法是养成习惯:Bean 的 package 声明和页面的 import 逐字对照一遍,通配符只跟在完整包名后面,改完 Java 类记得重新发布。

6. 复习清单:把六道题压成一张可自查的表

复习阶段最忌讳从头把代码再抄一遍。更有效的做法是拿一张表,按环节逐项打勾,把「我以为会」变成「我确认过」。下面这张表按提交前自查的顺序排,每一项都能在十秒内验证。

环节自查问题通过标准
项目结构页面在内容根目录、类在源目录吗访问路径能打开,Bean 能被 import
编码pageEncoding 与 contentType 都是 UTF-8 吗页面中文正常,响应头带 charset
脚本元素表达式标签后面有没有多余分号页面能编译,无语法错误
Bean 规范有 public 无参构造、属性是否私有useBean 能实例化,属性可读写
参数装配表单控件名与 Bean 属性名逐字一致吗setProperty 后属性不为 null
跳转成功用转发、失败用重定向,转发后 return 了吗地址栏行为符合预期,无重复提交
输出安全用户输入在输出时转义了吗输入尖括号后页面显示为文字而非标签

打勾的过程中如果某一项卡住,直接回到对应章节看那一段代码,比通读全文效率高得多。九个内置对象也值得顺手过一遍:request、response、out、session、application、pageContext、page、config、exception,其中前五个是上机题里真正会用到的,exception 只在错误页里出现,config 几乎不碰。

从那以后我每次交 JSP 上机作业前,都强制先跑一遍编码、Bean 装配、跳转这三项,再打开页面手动输入一段带尖括号的文本看转义是否生效——这三步花不到五分钟,却能挡掉绝大多数的返工。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询