☰
JSP教程光盘实践:从环境配置到部署排错的全路径解析
2026/10/11 13:20:38 网站建设 项目流程

简介:配套《JSP动态网站开发基础教程与实验指导》教材的光盘资源,面向初学JSP动态网站开发的读者,涵盖开发环境搭建、页面元素、内置对象、文件访问、数据库操作、JavaBean、Servlet以及HTML、CSS、JavaScript等基础知识,并包含宠物诊所系统、Web图书查询系统、购物系统三个综合案例。资源共480个文件,压缩包约30.73MB,以JSP源码、Java类与class字节码为主,辅以HTML页面、CSS样式、JavaScript脚本、XML配置及SQL数据库脚本,便于对照教材逐章练习与二次修改。目前已吸引730人学习,适合作为JSP课程实训、职业培训或自学参考资料。读者可获得全部实例源代码、视频教学文件、实验单元练习及综合项目实现,帮助从基础语法到完整项目开发逐步进阶。

1. 这张JSP教程光盘值钱的不是代码,是那条排好的实验路径

拿到一本《JSP动态网站开发基础教程与实验指导(从基础到应用)光盘》,很多人第一反应是打开光盘目录,把每章的例程代码拖到本地直接扔进Tomcat,结果多半是404、JDBC报错、中文乱码三连。问题不在代码,而在环境。这类光盘不是脚手架,它在设计时锁定了一套JDK、Tomcat、MySQL的版本组合,也按章节递进安排了知识点。它的价值不在某一段源码本身,而是那条从环境配置、静态页面、JSP语法,到表单交互、会话管理、购物车综合实验的路径。适合正在上JSP课的学生、要给学生布置实验的老师、以及想快速捡回JSP服务端基础的从业者。下面按这条路径展开,每一步都是可以直接照着做的。

2. 先把运行环境对齐:JDK、Tomcat、MySQL的三件套选型

光盘里的例程不是解压就能跑的,这一点卡住了大多数初学者。JSP页面第一次被浏览器访问时,Tomcat要把它翻译成一个Servlet源文件,再用javac编译成class,然后加载执行。翻译和编译都依赖JDK而不是仅仅一个JRE;Tomcat负责把JSP变成Servlet并管理它的生命周期;MySQL则承担购物车、登录、商品列表这些例程的数据存取。三者版本不匹配,报错往往让人看不懂,比如“无法加载驱动”或“类找不到”,其实问题出在版本组合上。

2.1 JSP页面一次请求的生命周期:先知道它跑在哪一层

浏览器请求一个index.jsp,Tomcat先查这个JSP有没有翻译过的Servlet类。如果没有,容器把JSP解析成Java源码,交给javac编译,再加载到JVM里实例化,然后调用service方法输出HTML响应。这个流程解释了为什么只装了JRE没有JDK时,第一次访问JSP会报编译相关错误;也解释了为什么改完JSP刷新就有变化,而改完Java类却要重启Tomcat。理解这条链路,排错时就知道该盯哪一层:编译期错误看日志里提示的java文件路径,运行期错误看异常栈里的Servlet类名。

常见做法是先用一个最小页面验证整条链路,再跑光盘例程。如果连一个只有一行输出的JSP都报错,那就是环境问题而不是业务代码问题。我一般会先建一个hello.jsp放到webapps下直接访问,通了再部署光盘内容,这样能把“环境坏了”和“代码坏了”分开排查。

2.2 版本匹配表与最小环境检查命令:别在Tomcat 10上硬跑老代码

光盘配套例程绝大多数按javax命名空间编写,这个细节直接决定Tomcat选型。Tomcat从10开始把javax.servlet迁到jakarta.servlet,老代码里的import javax.servlet.http.HttpServlet会直接编译失败。所以跑这类光盘,优先选Tomcat 8.5或9,而不是追最新版。

我一般会把版本匹配关系列成一张表,方便对号入座:

Tomcat版本Servlet/JSP规范推荐JDK对老光盘代码适配性
Tomcat 7.0Servlet 3.0 / JSP 2.2JDK 6-8最贴近教材年代
Tomcat 8.5Servlet 3.1 / JSP 2.3JDK 7-8兼容性好,最常用
Tomcat 9Servlet 4.0 / JSP 2.3JDK 8-11兼容,个别API有差异
Tomcat 10+Servlet 5.0+ / JSP 3.0+JDK 8+命名空间变更,老代码报错

选型逻辑一句话:老光盘配Tomcat 8.5或9,JDK配1.8,MySQL配5.7或8.0,驱动用对应版本。这套组合是兼容性最稳的,也是我在折腾过一堆版本号匹配的“玄学”之后才固定下来的选择。

环境装好后用下面这组命令做一次体检:

java -version javac -version echo $CATALINA_HOME $CATALINA_HOME/bin/version.sh

java和javac都存在,说明已装JDK而不是只有JRE;javac不存在,JSP第一次访问就会编译失败。version.sh会输出Tomcat的Server number与JVM版本,确认Tomcat跑在你指定的JDK上。这一步通过后,再用2.1节的hello.jsp做链路验证,环境就算对齐了。

2.3 把光盘的数据库脚本导入MySQL:两条命令的事

光盘里通常有一个database或sql目录,放着建库建表脚本。常见做法是建好一个专用数据库再导入脚本,避免和本机其他数据混在一起。先看一眼脚本开头有没有CREATE DATABASE语句,有的话直接导入,没有就先建库再导入。

mysql -u root -p -e "CREATE DATABASE shopdb CHARACTER SET utf8mb4;" mysql -u root -p shopdb < /path/to/cd/sql/shopdb.sql

第一条命令建库并指定utf8mb4字符集,第二条把脚本导入shopdb。导入如果报错,盯错误行号和错误码:1064通常是语法差异,常见于MySQL 5.x时代脚本在8.x执行,比如旧的排序规则写法;2003是连不上MySQL服务,先确认服务启动。导入完成后执行SHOW TABLES验证表结构,再把数据库名、用户名、密码写进光盘例程的JDBC连接串,这一步决定后面的登录页能不能查出数据。

注意:老光盘的JDBC连接串经常写死成localhost:3306,如果你换了端口或改了密码,记得同步修改,否则会出现“能部署但一查库就报错”的诡异现象。

3. 从光盘到浏览器:把第一个完整例程部署进Tomcat

环境三件套配齐后,才轮到光盘内容上场。直接整盘复制到某个目录双击运行是不可行的,JSP应用必须部署到Tomcat的webapps目录里,以特定目录结构存在。这一步走顺了,后面的实验才不会卡在“不知道把东西放哪”上。

3.1 先看懂光盘目录:例程、实验指导、依赖包各在哪

这类教材光盘的顶层结构通常很规整,常见做法是分几个大目录:codes或chapters放各章例程,document或实验指导放实验说明文档,database放SQL脚本,lib放例程需要的外部jar包。我见过的大多数光盘还会带一个readme或运行说明,写清楚每章例程使用的Tomcat版本和数据库配置。

光盘顶层目录典型内容使用时机
codes/chapters按章节编号的例程工程部署到Tomcat运行
实验指导文档每个实验的目标、步骤、验收点做完例程后对照自测
database/sql建库建表与初始数据脚本环境搭建阶段导入
lib/dependency数据库驱动、文件上传组件等缺类时复制到WEB-INF/lib

拿到光盘后第一件事不是看代码,而是把顶层目录列一遍,确认例程是war包还是散文件形式。war包直接复制到webapps即可,散文件则要手动建一个应用目录并放进去。这一步十来秒能完成,能省掉后面大量“文件放错位置”的返工。

3.2 部署例程到webapps的标准动作与正确访问姿势

Linux或macOS下,部署一个散文件例程的典型动作是把整个应用目录复制到webapps:

ls $CATALINA_HOME/webapps cp -r /media/cdrom/codes/chap03_shop $CATALINA_HOME/webapps/ ls $CATALINA_HOME/webapps/chap03_shop/WEB-INF

第一条命令看webapps下已有的应用,第二条复制例程目录,第三条确认WEB-INF目录存在。WEB-INF是Servlet规范要求的特殊目录,浏览器无法直接访问里面的文件,但Tomcat会读取其中的web.xml、classes和lib目录。如果复制过去后WEB-INF没带完整,Tomcat会把这个目录当成静态资源发布而不是应用,这是新手最常见的部署失败原因。

部署完成后访问地址是http://localhost:8080/chap03_shop/页面名,应用名必须和webapps下的目录名一致,大小写也一样。Windows用户不用cp,直接把文件夹拖进webapps即可,效果相同。改完JSP刷新浏览器就能看到新内容,因为Tomcat检测到JSP文件时间戳变化会自动重新翻译编译;改了WEB-INF/classes里的Java类就不一样,需要重启Tomcat或让reloadable="true"生效,这是老光盘例程里“改了怎么没效果”的头号原因。

3.3 第一个JSP实验:从静态页到动态页,代码哪里变了

光盘第一个实验通常是把一个静态页面改成JSP,差别就三处:page指令、表达式、声明。用一个最简例子说明:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import="java.text.SimpleDateFormat, java.util.Date" %> <html> <head><title>第一个动态页面</title></head> <body> <%! String getGreeting() { int hour = new Date().getHours(); if (hour < 12) return "上午好"; if (hour < 18) return "下午好"; return "晚上好"; } %> <p><%= getGreeting() %>,现在是 <%= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) %></p> </body> </html>

<%@ page %>是page指令,language固定java,contentType和pageEncoding都写UTF-8,避免中文乱码;<%! %>声明成员方法,在整个Servlet生命周期内可复用;<%= %>是表达式,把结果直接输出到响应;<% %>才是脚本片段,里面写多条语句。这个页面跑起来后,去Tomcat的work目录看一眼生成的_jsp.java,等于把JSP这个“黑匣子”当场拆开,比背十遍语法都直观。

第一次跑通时建议停下来做一个确认:把Hello World改成输出当前访问者的User-Agent或IP,确认request内置对象可用。这一步过了,说明JSP语法主链路已经打通,可以进入表单和会话环节了。

4. 从基础到应用的分水岭:表单、会话与文件上传

光盘前几章讲语法,到购物车这类综合实验时,突然就要求同时处理表单提交、会话保持、数据库读写,这是初学者最容易断层的地方。这三个能力点串起来,才算真正“从基础到应用”。

4.1 中文表单提交为什么总是乱码:过滤器是最省事的解法

表单提交有三种乱码来源:页面本身编码、请求编码、响应编码。页面编码由pageEncoding和contentType控制;POST请求编码由request.setCharacterEncoding控制,但它必须在第一次调用getParameter之前调用,否则不生效;GET请求的查询串编码更特殊,老版本Tomcat还要在server.xml的Connector上加URIEncoding="UTF-8"。这三处只要有任一处不一致,页面就是问号或菱形字符。

我一般会直接给光盘例程补一个编码过滤器,一劳永逸。Servlet 3.0以后支持注解注册:

@WebFilter("/*") public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("UTF-8"); res.setCharacterEncoding("UTF-8"); chain.doFilter(req, res); } }

@WebFilter(value="/*")把过滤器应用到所有请求,setCharacterEncoding在请求进入业务代码前就执行,天然满足“必须在读取参数之前调用”的要求。这个过滤器加进光盘例程时,类要放在WEB-INF/classes对应包目录下,编译后重启Tomcat。加上它之后,页面乱码问题基本能消灭九成,剩下的一成在数据库连接串,见5.1。

4.2 Session与Cookie:购物车场景里的分工

登录状态和购物车都靠Session,但Session本身依赖Cookie传递会话标识。JSP里session是内置对象,page指令默认session="true",所以页面里直接能用。初学者容易混淆的是生命周期:Session对象存在服务器内存里,默认超时30分钟;Cookie存在浏览器端,关闭浏览器后JSESSIONID丢失,但服务器上的Session对象不会立刻销毁,而是等到超时再回收。

购物车计数是最直观的入门例子:

<% Integer count = (Integer) session.getAttribute("cartCount"); if (count == null) count = 0; session.setAttribute("cartCount", ++count); %> <p>购物车当前商品数:<%= count %></p>

getAttribute从Session取出上次存的值,null说明第一次访问,setAttribute覆盖旧值。连续刷新页面计数会递增,关掉浏览器再打开会从1重新开始,这正好演示了“Session数据跟着浏览器会话走”的行为。光盘里的购物车实验通常还会把商品ID放进一个集合,再配合数据库查详情,原理和这个计数器一样,只是value从Integer变成了List或Map。动手时可以盯一个点:同一浏览器开两个标签页,Session在标签页间是共享的,因为JSESSIONID没变;两个不同浏览器之间则不共享。这比背概念直观得多。

4.3 文件上传:不引入框架也能实现的三个要点

文件上传在封面图、用户头像这些功能里几乎必用,也是JSP例程里最容易翻车的一块。表单必须写enctype="multipart/form-data",一旦写了这个,request.getParameter就拿不到普通字段了,因为请求体被multipart协议拆分成多个部件。这是一个经典的“为什么其他字段突然读不到”的坑。

处理上传有两种路子:用fileupload组件,或者手动读getInputStream解析。不引入框架时我基本用组件,核心代码放在一个Servlet的doPost里:

DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 1024, new File(tempDir)); ServletFileUpload upload = new ServletFileUpload(factory); List<FileItem> items = upload.parseRequest(request); for (FileItem item : items) { if (!item.isFormField()) { String fileName = new File(item.getName()).getName(); item.write(new File(saveDir, fileName)); } }

这段代码适用于Tomcat 9及以下、javax.servlet命名空间的光盘例程。三个易错点:parseRequest之前绝不能调用request.getParameter,否则multipart流被提前消费,解析出来是空列表;saveDir目录要预先创建,FileItem.write不会自动建目录;item.getName()在某些老浏览器里返回完整路径,要用new File(name).getName()截取文件名。这三点踩过一遍,文件上传基本就通了。

5. 光盘代码跑不起来的五个高频排查点

环境对了、部署也对了,不代表一定能跑起来。老光盘代码跑不起来的报错集中在几个固定原因,下面按出现频率列出来,每条都按现象、原因、解决讲清楚。

5.1 中文乱码:现象、原因与三步修复

现象:页面输出“???”,数据库里的中文读出来是乱码,提交表单后服务端拿到的是问号。原因:页面编码、请求编码、数据库连接串编码三处没有统一成UTF-8,任何一处不一致,都会在链路某一段发生转码错误。解决分三步走:第一步,所有JSP页面pageEncoding和contentType统一为UTF-8;第二步,按4.1加编码过滤器;第三步,JDBC连接串带编码参数:

jdbc:mysql://localhost:3306/shopdb?useUnicode=true&characterEncoding=UTF-8

useUnicode=true和characterEncoding=UTF-8成对出现,这是老驱动约定,缺一个都可能在写入时转成别的字符集。修改连接串后重启Tomcat再验证,只改页面不改数据库连接是无效修复。

5.2 类找不到与数据库驱动加载失败:jar包要放对地方

现象:部署成功但一运行就抛ClassNotFoundException或NoClassDefFoundError,常见的是com.mysql.jdbc.Driver这一类。原因:驱动jar没有放进WEB-INF/lib。很多人把jar丢在webapps的某个子目录,或者放在工程src目录里,Tomcat根本不会去扫这些位置。解决就一条,把jar复制到当前应用目录的WEB-INF/lib下,再重启:

cp mysql-connector-java-8.0.x.jar $CATALINA_HOME/webapps/应用名/WEB-INF/lib/

同时注意一个隐蔽情况:WEB-INF/lib里如果同时存在两个版本的驱动jar,JVM按类路径顺序随机加载其中一个,表现是“有时能连上、有时报驱动类错误”,这种时好时坏的问题,检查lib目录文件列表后基本都能发现。

5.3 Tomcat 10与老代码的命名空间冲突

现象:光盘代码在Tomcat 10上启动,编译期直接报错,import javax.servlet.*的语句全部找不到包。原因:Tomcat 10开始Servlet API的包名从javax迁到了jakarta,老代码按javax写,类路径里根本没有这个包。解决:最省事的是换Tomcat 9或8.5重新跑,别指望靠改配置兼容;如果非要用Tomcat 10跑新代码,把import和注解里的javax改成jakarta。这个操作只对你有源码的例程有意义,光盘里已编译好的class没法自动迁移。

5.4 数据库连接被拒与端口占用:两条日志定位路径

现象一:启动后一查数据库就报Connection refused或Cannot create PoolableConnectionFactory,日志指向MySQL。原因:MySQL服务没启动,或连接串里的密码、端口不对。Windows用services.msc查MySQL服务状态,Linux用systemctl status mysql查。另一个高频点是驱动版本:MySQL 8.x环境里还塞着老光盘配套的5.x驱动,会报SSL连接或时区错误,换成mysql-connector-java 8.x并在URL后加serverTimezone=Asia/Shanghai即可。

现象二:Tomcat启动报Address already in use: JVM_Bind。原因:8080被其他进程占用,或者上一次Tomcat没正常退出还占着端口。解决:先查占用再决定是杀进程还是改端口:

lsof -i :8080 netstat -ano | findstr :8080

lsof适用于Linux和macOS,netstat适用于Windows,拿到PID后按系统方式结束进程。不想杀进程就改conf/server.xml里Connector的port为8081,注意改完后所有访问URL的端口都要跟着变,这个细节经常被忽略。定位这类问题,我的习惯是先看catalina.out和localhost.log两个日志文件,绝大多数连接和端口报错都会同时出现在这两处,比瞎猜快得多。

6. 实验做完不算会:把例程拆成两层,再自己加一个分页

光盘里的实验全部跟着做完、逐个跑通,这算会用,但离会写还有一段距离。我的建议是找一个已经跑通的列表页例程,做一次“拆层”练习:把JSP里直接写JDBC的Scriptlet代码全部挪走,数据库操作收进DAO类,请求处理交给Servlet,JSP只负责用表达式输出数据。拆完之后你会发现,原来的JSP从一百多行脚本变成二十多行标签,出了问题也知道该去哪一层找。

拆层后给自己加一个分页功能,这是验证基本功最划算的练习。分页的核心是两条SQL:

SELECT COUNT(*) FROM products; SELECT * FROM products ORDER BY id DESC LIMIT ?, ?;

对应Java端的参数设置:

int pageSize = 10; int pageNo = Integer.parseInt(request.getParameter("pageNo")); String sql = "SELECT * FROM products ORDER BY id DESC LIMIT ?, ?"; pstmt.setInt(1, (pageNo - 1) * pageSize); pstmt.setInt(2, pageSize);

LIMIT的两个参数含义:第一个是偏移量,从第几条开始取;第二个是取多少条。pageNo由页面传来,偏移量是(pageNo-1)×pageSize。注意pageNo要做防御:前端传负数、非数字、超大的页数都要兜住,常见做法是try-catch后默认pageNo=1,再和总页数取较小值。总页数用COUNT(*)算一次,除以每页条数后向上取整即可。

这个练习做完,建议再回到光盘最后一个综合实验,把购物车不用示例代码从头自己写一遍。我当年啃这类教材光盘时,一口气把所有例程跑通,以为学会了,过了一段时间要手写一个带分页的列表页,脑子里一片空白。后来耐下心把两个例程拆层重写,才算真正建立起JSP应用的骨架。如果你也卡在“跑通了但自己写不出来”的阶段,别急着囤下一个教程,选一个例程做拆层加翻页,卡住了再回头翻实验指导,效果比刷一堆新课都实在。希望帮到你。

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

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

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

立即咨询