news 2026/10/2 20:20:22

SSM+JSP+Layui电影在线观看系统:环境配置、代码走读与部署避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSM+JSP+Layui电影在线观看系统:环境配置、代码走读与部署避坑指南

简介:这是一套基于 Java SSM 框架的电影在线观看系统项目源码,主要面向 Java Web 初学者、毕业设计及课程设计人群,帮助解决从零搭建可运行项目、梳理 Spring、SpringMVC、MyBatis 整合逻辑,掌握 JSP 页面与 Layui 前端组件联调的问题。压缩包为 RAR 格式,共 322 个文件,大小约 4.32MB;其中包含 35 个 Java 类文件,按实体、控制器、业务服务、数据访问等分层组织,29 个 JSP 页面用于前端展示,31 个 XML 文件承担 Spring 配置及 MyBatis 映射,30 个 JS、15 个 CSS 和若干图片共同构成 Layui 界面资源,另附 SQL 脚本用于初始化数据库,并有 75 个 GIF 动态演示图,方便直观了解登录、注册、电影列表、播放与历史记录等页面效果。项目基于 JDK1.8、Tomcat8.5 和 MySQL,兼容 Eclipse、IntelliJ IDEA、MyEclipse 等常见开发环境,利用 Maven 管理依赖,导入后按说明配置即可启动。目前已有 551 人浏览学习,适合拿来直接运行演示,也适合二次开发或作为答辩讲解时的完整参考。

1. 电影在线观看系统:一套能跑通的 SSM + JSP + Layui 组合

电影在线观看系统是一套用 Java 语言、基于 SSM(Spring + SpringMVC + MyBatis)三层架构、前台用 JSP 服务端渲染、后台用 Layui 搭建管理界面、依赖由 Maven 统一管理、数据落在 MySQL 的完整 Web 项目。它不是那种打开只能看几张静态截图的演示工程——用户登录、视频列表、分类筛选、播放页、后台管理、浏览历史这些功能,在代码里都有对应的 Controller、Service、Mapper 实现。下载到本地后,按环境说明装好 JDK 1.8、Tomcat 8.5 和 MySQL,导入 Eclipse 或 IntelliJ IDEA 就能启动访问。它适合正在写 JavaWeb 课程设计、准备毕业设计答辩,或者想拿一套传统 SSM 项目练手以应对面试的人。你要的不是 PPT 式的架构图,而是一个能本地跑起来、能打断点、能看着数据一条条从 JSP 流向 MySQL 的真实系统。

2. 技术选型拆解:为什么是 SSM + JSP 而不是 SpringBoot + Vue

现在一搜 springboot2 集成 jsp、maven 安装配置、mysql 连接池这类词,出来的教程大多围着新框架转,但回到毕设、课设和老系统维护场景,SSM + JSP 依然是绝对主力。SpringBoot 自动配置把 Spring 容器、DispatcherServlet、MyBatis 的组装过程全部藏起来了,你调试时看到的是一个启动类加一堆注解,容器怎么创建、拦截器何时生效全是黑匣子。SSM 项目把这些全部摊在 web.xml、spring-mvc.xml、spring-mybatis.xml 里,每一处配置都在回答一个问题:容器启动时到底干了什么。再加上 JSP 能直接在页面里用 EL 和 JSTL 取后台数据,不需要单独起一个前端工程做跨域联调,对于课程设计、毕业设计、老系统维护这些场景,反而比前后端分离更省事。

2.1 先从现有类看架构:Controller、Service、Mapper 三层的分工边界

拿到项目别急着启动,先看类。这个项目的类结构很清晰:VideoController、UserController、ManagerController 是表现层入口,负责接收浏览器请求、调用业务服务、决定返回视图名还是 JSON;VideoService 接口加 VideoServiceImpl 是业务层,负责校验参数、组织多个 Mapper 调用、处理事务;VideoMapper 是 MyBatis 的映射接口,它在 XML 里声明 SQL 并执行数据库操作。实体类 Video、User、History 分别对应视频表、用户表、浏览历史表,是三层之间传递数据的载体。ManagerController 单独拆出来的意义在于后台管理功能需要独立的权限控制,而前台 UserController 和 VideoController 对应登录用户和游客的两类访问需求。

理解分层最有效的方式是把一次请求当作流水线:JSP 页面发起请求,DispatcherServlet 找到 Controller 方法,Controller 调 Service 接口,Service 实现类里注入 Mapper,Mapper 执行 SQL 返回结果,再逐层反向包装。VideoController 里注入的是 VideoService 接口而不是 VideoServiceImpl,这是依赖倒置——Spring 容器启动时把实现类实例化之后注入,以后替换实现类不用改 Controller 代码。很多新手第一次看这个结构觉得绕,但正是这层间接性让项目具备基本可维护性。

2.2 环境配置的硬指标:JDK 1.8、Tomcat 8.5、MySQL 该怎么配对

这个组合不是随便选的。JDK 1.8 是 Spring 4.x 和 MyBatis 3.x 时代最成熟的运行版本,Spring 老版本在 JDK 9 以上会遇到模块化反射报错;Tomcat 8.5 对应 Servlet 3.1 规范,包名还是 javax.servlet,而 Tomcat 10 已经整体换成 jakarta.servlet,老项目直接部署会看到 ClassNotFoundException;MySQL 用 5.7 或 8.0 都行,但 8.0 必须处理驱动版本和时区参数。这套组合表面保守,实际目标就是稳定。

组件推荐版本关键注意点
JDK1.8(建议 8u201 以上)JDK 11 起输出非法反射访问告警,部分老库直接报错
Tomcat8.5.x不要用 Tomcat 10,javax.servlet 包名不兼容
Maven3.6.x3.8+ 对中央仓库访问限制更严格,配置文件要同步调整
MySQL5.7 或 8.08.0 必须在 JDBC URL 里指定 serverTimezone
IDEIDEA / Eclipse / STS编译级别必须显式指定 1.8,否则运行时版本错乱

这张表动手前值得放在手边。我见过有人拿 JDK 11 跑老 SSM,启动时 Spring 容器告警但项目也起来了,以为是玄学,结果跑到 MyBatis 的 CGLIB 代理时直接抛异常,原因是依赖了被删除的内部 API。版本这事不解决,后面每一步都在给前面的错误买单。

2.3 Maven 依赖管理:把 pom.xml 当项目的“购物清单”来读

Maven 在这里的作用有两个:引入依赖和打包构建。你不需要手动下载 Spring jar 包塞进 WEB-INF/lib,只要在 pom.xml 里声明坐标,Maven 就会自动拉取。这个项目常见的依赖有 spring-webmvc、spring-jdbc、mybatis、mybatis-spring、mysql-connector-java、jstl、jackson-databind。下面是我按这套结构整理出的核心依赖配置:

<properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>4.3.30.RELEASE</spring.version> <mybatis.version>3.4.6</mybatis.version> </properties> <dependencies> <!-- SpringMVC 与 Spring JDBC 支持 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- MyBatis 与 Spring 整合 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency> <!-- MySQL 驱动,运行时需要 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> <scope>runtime</scope> </dependency> <!-- JSTL 标签库与 Jackson JSON 处理 --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.10</version> </dependency> </dependencies>

参数上几个版本号值得说明:spring.version 用 4.3.30 而不是 5.x,因为 Spring 5 对 JDK 版本和包结构的要求跟 JSP 项目不完全匹配;mybatis-spring 1.3.2 配套 MyBatis 3.4.6 是长时间验证过的组合,自行升级容易触发 SqlSessionFactoryBean 的兼容问题。mysql-connector-java 5.1.49 支撑 MySQL 5.7,如果换 MySQL 8.0,驱动版本要升到 8.0.x,同时 JDBC URL 里必须加 serverTimezone=Asia/Shanghai。jackson-databind 是 Layui 后台表格必需的,Ajax 请求的 JSON 序列化全靠它。JSTL 1.2 为 JSP 页面里的 c:forEach 标签提供支持,少它页面直接报标签找不到。

看 pom.xml 的重点永远是版本号而不是有没有依赖。版本冲突是 SSM 项目启动失败最常见的原因,具体现象和排查方法放在第 5 章避坑部分展开。

3. 把代码跑起来:从导入工程到数据库落库的完整路径

3.1 数据库初始化:先建库建表,还是先启动项目?

我的习惯是先把数据库建好再启动项目。这个系统核心数据是 user 用户表、video 视频表、history 浏览记录表,项目 resources 目录下一般自带 .sql 脚本,用 Navicat 导入即可。如果没找到现成脚本,可以按下面这套结构建库:

CREATE DATABASE IF NOT EXISTS movie_online DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE movie_online; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(64) NOT NULL, `nickname` varchar(50) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `video` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL, `cover` varchar(500) DEFAULT NULL, `url` varchar(500) DEFAULT NULL, `category` varchar(50) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `history` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `video_id` int(11) NOT NULL, `watch_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

建表时几个细节是刻意为之:user 表的 username 加唯一索引,对应注册时用户名不可重复的业务校验;password 字段设成 varchar(64),是因为 MD5 存 32 位密文、SHA-256 存 64 位,预留空间方便日后升级加密方式,不用再改表结构;history 表给 user_id 建索引,是先把“查某用户最近看过哪些视频”这条高频查询撑住。表结构设计直接决定 Mapper 里的 SQL 怎么写,Java 实体类的字段名也是跟着表结构走的。

网上很多教程把启动顺序弄反,先跑 Java 再建库,结果启动时报找不到表又回来补,来回折腾多花半小时。video 表的 url 和 cover 如果是本地文件路径,还需要在 SpringMVC 配置里加静态资源映射,这部分放到 3.2 说。

3.2 SpringMVC 配置与静态资源:启动后页面 404 的真正原因

SSM 项目里一类最容易忽略的是 spring-mvc.xml 的静态资源映射。JSP 页面本身由 JSP/Servlet 容器处理没问题,但 Layui 的 CSS、JS、图片以及上传的封面视频这些静态文件,如果被 DispatcherServlet 拦截,就会全部变成 404。这个问题在初次启动时出现的概率极高,而且报错信息不直观。

<!-- spring-mvc.xml 关键配置 --> <mvc:annotation-driven /> <mvc:default-servlet-handler /> <!-- 静态资源目录映射 --> <mvc:resources mapping="/static/**" location="/static/" /> <mvc:resources mapping="/upload/**" location="file:/data/movie_online/upload/" /> <!-- JSP 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean>

参数含义:prefix 和 suffix 决定 Controller 返回 video/list 时,SpringMVC 实际渲染 /WEB-INF/views/video/list.jsp。WEB-INF 目录下的 JSP 浏览器无法直接访问,必须经过 Controller 转发,这本身就是一层安全设计。mvc:resources 的 location 如果带 file: 前缀,说明静态文件在服务器磁盘上而不是工程目录,生产环境这么用很常见,本地调试时要改成自己机器的实际路径。mvc:default-servlet-handler 是兜底方案,处理那些没被 resources 覆盖的静态请求。

Tomcat 端口默认 8080,端口被占用时改 conf/server.xml 里的 port 即可,但还要注意项目上下文路径。传统 JSP 项目部署后的访问路径是 http://localhost:8080/项目名/xxx,比如 http://localhost:8080/movie_online/login。想不带项目名直接访问,得在 IDE 的 Deployment 里把 Application context 改成根路径。项目文档写的是 Jdk1.8 + Tomcat8.5 + MySQL,按这个范围配最稳。

3.3 从浏览器到数据库:一条登录请求的完整链路

项目跑起来后,第一个应该验证的功能是登录。把浏览器的网络面板当作黑盒观察:输入用户名密码点登录,表单 POST 到 /user/login,DispatcherServlet 找 UserController 的方法,方法注入 UserService 接口,UserServiceImpl 调 UserMapper,Mapper 代理执行 SQL,返回 User 对象,密码校验通过后把 User 放进 session。这一条链路就是 SSM 程序的调试基准,后面所有问题都能归位到其中某一个节点。

@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(String username, String password, HttpSession session) { User user = userService.login(username, password); if (user == null) { return "redirect:/login.jsp?error=1"; } session.setAttribute("loginUser", user); return "redirect:/index"; } }

逻辑说明:登录方法接收表单提交的 username 和 password,SpringMVC 会自动完成参数绑定。userService.login 返回 null 表示用户名或密码不匹配,重定向到登录页并带 error 参数;登录成功则把 User 对象放进 session,后续要拿当前登录人直接从 session 取。return 的字符串配合视图解析器最终指向 index.jsp,这里的 redirect 前缀是让浏览器重新发起一次 GET 请求,而不是服务端转发。

调试这种链路我推荐 Chrome 的 F12 面板:先看 Network 里请求有没有发出、状态码是什么;再看 Console 里有没有 JS 报错,SSM 项目前端报错大部分是路径不对或字段名不一致;最后才打开 Controller 断点看内部执行。断点比加日志快得多,因为一次断点就能定位问题到底在 Controller 段、Service 段还是 Mapper 段。

4. 核心代码走读:视频服务、用户登录与浏览记录的关键实现

4.1 VideoController:参数透传和视图返回的两种写法

VideoController 是这个系统最核心的入口,要处理视频列表、视频详情、分类查询,以及后台管理操作。前台页面需要完整 HTML 渲染时,Controller 返回视图名,配合 JSP 的 EL 和 JSTL 把数据展示出来;后台管理页面用 Layui 表格拉数据时,Controller 返回 JSON。两种方式在同一个 Controller 里共存,但方法上的 @ResponseBody 要分清楚。

@Controller @RequestMapping("/video") public class VideoController { @Autowired private VideoService videoService; // 前台视频列表页,返回视图名 @RequestMapping("/list") public String list(String category, Model model) { model.addAttribute("category", category); return "video/list"; } // 后台管理接口,Layui 通过 Ajax 拉 JSON 数据 @RequestMapping("/adminJson") @ResponseBody public LayuiResult videoJson(Integer page, Integer limit, String keyword) { List<Video> list = videoService.findVideos(page, limit, keyword); long count = videoService.countVideos(keyword); return LayuiResult.success(count, list); } }

参数说明:list 方法没有直接查数据库,它把 category 放进 Model 后返回视图名,真正渲染发生在 JSP 页面里,页面通过 ${category} 读取参数再发起数据请求。这种写法在传统 JSP 项目中很常见,好处是前台 URL 可以被搜索收录,坏处是页面和数据加载逻辑耦合。videoJson 是典型后台接口,@ResponseBody 让 SpringMVC 把返回值序列化成 JSON,Layui 的 table 组件要求返回结构包含 code、msg、count、data 四个字段,所以这里封装了 LayuiResult 适配。

这段被写歪的概率极高,最常见的是把 page、limit 参数写死,查出全量数据扔给前端分页,数据量一大页面卡死。正确做法是让 Layui 通过请求把 page 和 limit 传过来,Service 层拼接 LIMIT,配合 countVideos 返回总数,这才是真正的服务端分页。

4.2 VideoServiceImpl:业务层最容易出问题的是事务和判空

VideoServiceImpl 承载了业务规则,比如新增视频时的参数校验、视频列表的分页查询。真正上过线的 SSM 项目里,这一层通常暴露出三个问题:事务边界不清晰、参数判空不严谨、SQL 拼接太随意。

@Service public class VideoServiceImpl implements VideoService { @Autowired private VideoMapper videoMapper; @Override @Transactional public void addVideo(Video video) { // 先做参数兜底校验,再落库 if (video.getTitle() == null || video.getTitle().trim().isEmpty()) { throw new IllegalArgumentException("视频标题不能为空"); } videoMapper.insert(video); } @Override public List<Video> findVideos(Integer page, Integer limit, String keyword) { // 分页参数兜底,防止 LIMIT 子句出现 null int offset = (page == null || page < 1) ? 0 : (page - 1) * (limit == null ? 10 : limit); int size = (limit == null || limit < 1) ? 10 : limit; return videoMapper.selectVideos(offset, size, keyword); } }

逻辑说明:@Transactional 给 addVideo 开启了数据库事务,插入 video 表以及后续的任何关联写操作都在同一个事务里,任何一个环节抛 RuntimeException,已经执行的 SQL 都会被回滚。这里用 IllegalArgumentException 表达参数不合法,而不是返回 false 或 null,因为问题出在调用方,应该让异常直接抛出并中断流程,由全局异常处理器统一转成页面提示。

findVideos 里的 offset 和 size 双兜底属于防御性编程:Layui 默认传 page=1、limit=10 没错,但你不能保证后续接手的人每次调用都把参数带全,limit 为 null 时直接拼进 MySQL 的 LIMIT 子句就会报语法错误。再往上提一步,可以给 size 设上限,后台管理页没必要一次性显示上千行,默认 10、最大 100 是比较合理的约束。另一个常见坑是 category 参数直接拼进 WHERE 子句,字符串里带个单引号就能改变查询语义,条件参数一律用 #{} 占位让 MyBatis 走预编译。

4.3 User 与 History:登录会话和浏览历史的数据关联

User 和 History 是用户体系的左右手。User 实体包含 id、username、password、nickname、createTime,登录成功后整个 User 对象放进 session,后续浏览历史、个人中心、上传记录都从 session 拿用户 ID;History 记录用户看过的视频,表里同时有 user_id 和 video_id,还带 watch_time 字段用于排序。查浏览历史的典型 SQL:

SELECT v.*, h.watch_time FROM history h LEFT JOIN video v ON h.video_id = v.id WHERE h.user_id = #{userId} ORDER BY h.watch_time DESC LIMIT #{limit}

逻辑说明:用 LEFT JOIN 而不是 INNER JOIN 是刻意的——用户历史记录里可能包含被下架或删除的视频,INNER JOIN 会把那些历史记录连带过滤掉,用户看到“历史记录莫名消失”是很差的体验。LEFT JOIN 保证 history 行一定保留,video 字段为 null 交给业务层判断即可。ORDER BY watch_time DESC 配合 LIMIT 实现“最近看过”的功能,这就是带时间字段中间表的优势:既能做“是否看过”判断,也能做时间线排序。

从实体关系能看出,这个系统的骨架是用户体系和内容体系两棵树,History 在中间把两者关联起来。ManagerController 负责后台管理,管理端页面要求全部先登录,这种拦截在 SSM 项目里通过 mvc:interceptors 配置,未登录请求会被重定向到登录页。部署之后务必确认拦截没失效,否则后台接口裸奔,任何人都能往里写数据。

5. 避坑与常见问题:SSM + JSP 项目部署运行里的五个典型翻车现场

这一章写的是实际部署这套项目时遇见的典型问题,每条按“现象 → 原因 → 解决”拆开。搜索里常见 jsp 入门、maven 配置、mysql 安装相关的问题,大部分能在下面五个场景里对号入座。

5.1 现象:JSP 页面直接输出了源码而不是渲染结果

现象:浏览器访问页面时看到 JSP 源文件内容,Java 代码和 HTML 标签混在一起直接显示,完全没有经过 JSP 引擎编译。

原因:要么是请求的路径没被 Tomcat 的 JSP Servlet 处理,要么是 web.xml 把某些扩展名错误映射给了 SpringMVC,要么是直接以物理文件方式打开了 JSP 而没有走 Tomcat。

解决:确认请求地址是 http://localhost:8080/项目名/xxx.jsp,而不是用 file:// 方式打开;检查 web.xml 里 DispatcherServlet 的 url-pattern,如果是/这种全拦截,必须加 mvc:default-servlet-handler 放行 JSP;再确认 Tomcat 是完整安装版,不是某些精简容器。这个问题在 IDEA 里最常被触发,因为 IDE 可能把 JSP 文件映射到了静态资源处理器。

5.2 现象:启动时报找不到 DispatcherServlet 类

现象:Tomcat 启动后直接 404,日志里出现严重错误,提示 ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet。

原因:项目没有把 Maven 依赖打包进部署目录,IDE 自动发布时 WEB-INF/lib 下缺少 spring-webmvc 这些 jar 包。IDE 的 Run on Server 有时候只复制了编译输出和项目文件,并没有做 Maven 依赖收集。

解决:在 Maven 面板执行 clean 和 package,用打出的 WAR 包部署,不要依赖 IDE 临时发布;也可以在 pom.xml 里配置 maven-war-plugin,确保 WEB-INF/lib 收集所有依赖。养成部署前在 target 目录看一眼 jar 包的习惯,能省掉一大半部署问题。

5.3 现象:Layui 表格接口返回了 JSON,列表却一直是空白

现象:浏览器 Network 面板里接口正常返回了 JSON 数据,看起来一切正常,但 Layui 表格一行都没渲染。

原因:Layui 的 table 模块默认解析{"code":0,"msg":"","count":100,"data":[...]}这种结构,如果 Controller 返回的是 List 或自定义 Map,字段名对不上,表格拿不到 data 数组,页面自然空白。

解决:统一封装一个返回对象,保留 code、msg、count、data 四个字段;或者在 table.render 里配置 parseData 回调做字段映射。后端封装一个通用 Result 类是更省事的方案,所有接口返回同一结构,前端只写一次解析逻辑,后续新增接口也不会再踩这个坑。

5.4 现象:MyBatis 报 Invalid bound statement 绑定异常

现象:调用 Service 层时日志抛 BindingException: Invalid bound statement (not found): com.xxx.mapper.VideoMapper.selectVideos。

原因:Mapper 接口编译了,但对应的 XML 映射没进 classes 目录。常见三种情况:XML 文件放在 src/main/java 下没做 resources 打包;spring-mybatis.xml 里 mapper-locations 路径和实际位置对不上;mybatis 相关依赖被引入多个版本导致 MapperRegistry 注册异常。

解决:检查 target/classes 下是否存在 VideoMapper.xml;确认 mapper-locations 用的是 classpath*:mapper/*.xml 这类通配写法;用 mvn dependency:tree 查 mybatis 的依赖树,排除重复 jar。这套排查顺序对 SSM 项目通用,不只限于这一个模块。

5.5 现象:数据库连接失败或中文写入变成问号

现象:启动时抛 Communications link failure,或者页面上输入中文保存后数据库里全是问号。

原因:MySQL 8.0 的 JDBC URL 没带 serverTimezone,驱动与服务器时区不一致直接拒绝连接;或者数据库字符集、表字符集、JDBC URL 字符编码三层不统一,中文数据在传输过程中被转义丢失。

解决:JDBC URL 统一写成jdbc:mysql://localhost:3306/movie_online?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai;建库建表用 utf8mb4 而不是 utf8,因为 MySQL 的 utf8 实际是 utf8mb3,存生僻字或表情会丢;JSP 页面头部加<%@ page contentType="text/html;charset=UTF-8" %>,Layui 的 Ajax 请求字符集保持一致。

这五个现场看着琐碎,每一条背后都是真金白银的调试时间。项目能顺利跑起来,一半靠代码,一半靠环境和配置,而且环境配置的优先级更高——代码一行没动,环境对了它就能跑;环境不对,改代码也没用。

6. 进阶:WAR 打包部署和三个值得动手改的优化点

6.1 用 Maven 打出 WAR 包而非依赖 IDE 部署

传统 JSP 项目最终交付形态是一个 WAR 包。在 pom.xml 里确保 packaging 是 war,执行 mvn clean package 后,target 目录下就生成了部署包,把它扔到 Tomcat 的 webapps 目录,启动即自动解压部署。

mvn clean package -DskipTests

参数说明:clean 清理 target 残留,避免旧 class 和新代码混合导致诡异报错;-DskipTests 跳过测试编译,加快打包速度。生成的 WAR 名默认是 artifactId-version.war,访问路径为 http://localhost:8080/artifactId-version/。想去掉版本号,在 pom.xml 的 build 节点里配置 finalName 为项目名即可。

6.2 部署成功必须验证的三个信号

验证部署是否成功,我一般不看启动完成日志,而是按这个顺序查三个信号:webapps 下自动解压出了和 WAR 同名的目录,里面 WEB-INF/lib 有完整的 jar 包;Tomcat 的 logs/catalina.out 没有 SEVERE 级别的异常记录;浏览器访问登录页,F12 控制台没有红色报错,用测试账号登录后数据列表能正常加载。三个信号全绿再交付,少一个都得回头查。

这套项目里我觉得最值得动手改的三个优化点:第一,把数据库连接信息抽到 jdbc.properties 里用占位符引用,而不是散落在 spring-mybatis.xml,方便切换环境;第二,给 user 表密码加盐,用 MD5 或 SHA-256 存密文,课程设计和毕设里明文密码是必扣分项;第三,视频列表加 Spring Cache 缓存,查询结果优先命中缓存再落库,视频网站这类读多写少场景收益特别明显。

从那以后,我每次拿到传统 JSP 项目,都强制自己先走一遍“看解压目录 → 看日志 → 看页面控制台”的动作,再开始改代码,几乎是本能反应。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 20:16:35

工控现货实战指南:PLC、变频器与伺服驱动器的应急替换与避坑策略

“工控现货”这四个字&#xff0c;外行人看了可能觉得没什么特别&#xff0c;但真正泡在工厂产线和自动化设备堆里的朋友&#xff0c;应该都懂它背后的分量。我见过太多半夜打来电话的客户&#xff0c;产线停了&#xff0c;手头的PLC模块烧了&#xff0c;原厂订货周期六到八周起…

作者头像 李华
网站建设 2026/10/2 20:15:44

SqlSugar联表查询实战:从Join到分页的完整指南

写SqlSugar联表查询这几年&#xff0c;我最大的感受是&#xff1a;查询本身不难&#xff0c;但真正写得好的人不多。尤其是在做报表、订单列表、后台管理系统时&#xff0c;一张主表要带出用户昵称、商品名、分类名&#xff0c;甚至还要带上子表汇总数据&#xff0c;如果还在手…

作者头像 李华
网站建设 2026/10/2 20:13:57

MySQL IFNULL()函数详解:从NULL处理到索引失效的实战指南

1. 先搞清楚一件事&#xff1a;NULL不是空值&#xff0c;是“未知” 在聊IFNULL()函数之前&#xff0c;我想先花点篇幅说说NULL本身。因为我在社区和实际工作里接触过太多人&#xff0c;SQL写了好几年&#xff0c;对NULL的理解还是错的。最常见的误解就是“NULL就是空嘛&#x…

作者头像 李华
网站建设 2026/10/2 20:13:56

AI工具trae到底好不好用?从配置文件到TaoToken接入的实测拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 20:13:53

需求侧电能共享分布式交易:价值认同建模与ADMM求解

去年帮课题组把"基于价值认同的需求侧电能共享分布式交易策略"从论文标题复现成能跑出结果的Matlab程序时&#xff0c;我最大的感受是&#xff1a;这个方向真正要处理的&#xff0c;不是"电不够分"的问题&#xff0c;而是"交易语言太粗糙"的问题…

作者头像 李华