ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java电影网站实战:Spring Boot+MyBatis+Thymeleaf完整项目

Java电影网站实战:Spring Boot+MyBatis+Thymeleaf完整项目 简介这是一套基于SSM框架与Vue前端的完整电影网站系统源码面向Java Web初学者与课程设计学生解决毕业设计、实训项目或Web全栈开发入门实践需求。资源包含880个文件涵盖144个Java后端业务逻辑与控制器代码、53个Vue组件及页面、167个JS交互脚本、55个CSS样式文件、79个GIF动效素材及38个JPG/PNG图片资源整体压缩包18.85MB结构清晰便于按模块用户管理、影片展示、素材上传快速定位学习。已有2594人下载学习配套含数据库SQL脚本、三套批处理启动脚本install/run/build、Bak备份文件及完整目录文档含摘要、章节结构与技术选型说明特别适合通过运行-调试-二次开发方式掌握前后端分离开发流程、MyBatisPlus集成、ElementUI组件应用及MySQL 5.7数据建模实践。1. 用 Java 搭建一个真实可用的电影网站不是 Demo而是能跑起来、查得准、改得动的 Web 项目你搜“电影网站 Java 源码”看到的大多是空壳首页、硬编码的几部电影、连数据库都懒得配的“课程设计”。但真实场景里一个合格的基于 Web 的电影网站必须支持用户浏览分类、按关键词搜索影片、查看详细信息导演、演员、评分、简介、后台管理新增/下架影片还要能应对并发访问、防止 SQL 注入、适配主流浏览器。它不是 Spring Boot 自动生成的 CRUD 模板而是把 JSP/Servlet 或 Thymeleaf MyBatis MySQL 这套经典 Java Web 技术栈真正串起来——从web.xml配置到MovieService接口定义从movie_list.jsp的分页渲染到MovieController的参数校验逻辑。适合刚学完 JDBC 和 Servlet 的开发者动手复现也适合 Java 面试者拆解其中的 DAO 层设计模式、事务控制点和 XSS 防御细节。本文不讲理论堆砌只聚焦怎么让这个“电影网站”在本地 Tomcat 里启动成功、数据能增删查改、前端页面不报 404、搜索结果不乱码。2. 用 Spring Boot Thymeleaf MyBatis 快速构建电影网站后端骨架2.1 为什么选 Spring Boot 而不是传统 Servlet——降低配置成本聚焦业务逻辑传统 Java Web 项目需手动配置web.xml、pom.xml中引入大量 jar 包servlet-api、jstl、commons-dbcp、编写MovieDAO的 JDBC 模板代码。而 Spring Boot 通过spring-boot-starter-web、spring-boot-starter-thymeleaf、spring-boot-starter-mybatis三个 starter自动完成内嵌 Tomcat 启动、视图解析器注册、MyBatis SqlSessionFactory 创建。更重要的是它规避了dsh web authentication required; reopen the url printed by dsh web.这类因环境变量缺失或 Web 容器未正确加载导致的启动失败——Spring Boot 默认监听8080端口无需额外配置server.xml且SpringBootApplication注解自动扫描Controller和Mapper避免手写contextConfigLocation。对于“电影网站设计与实现”这类教学型项目Spring Boot 的约定大于配置特性能让开发者把精力放在Movie实体字段设计、MovieMapper.xml的select语句优化上而不是调试 classpath 冲突。2.2 初始化项目结构Maven 依赖与关键配置文件创建 Maven 项目后在pom.xml中声明核心依赖版本号取 Spring Boot 2.7.x 兼容稳定版dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.2.0/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies提示spring-boot-starter-validation用于后续对电影名称、年份等字段做NotBlank、Min(1900)校验避免空字符串插入数据库mysql-connector-java的runtime作用域确保打包时不含驱动运行时由容器提供。application.yml配置数据库连接与 MyBatis 映射路径spring: datasource: url: jdbc:mysql://localhost:3306/movie_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false # 开发期禁用模板缓存修改 HTML 立即生效 enabled: true mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true # 自动将数据库 snake_case 字段映射为 Java camelCase 属性2.3 设计 Movie 实体与数据库表字段必须覆盖真实电影网站需求电影网站源码若只含id,name,year三字段无法支撑“按类型筛选”“按评分排序”“演员关联查询”等常见功能。实际movie表应包含字段名类型说明idBIGINT PK主键自增titleVARCHAR(200)电影中文名非空original_titleVARCHAR(200)原文名如 The Shawshank Redemption支持多语言yearINT上映年份范围 1900–2030directorVARCHAR(100)导演支持多人逗号分隔actorsTEXT主演列表JSON 数组格式存储 [Tim Robbins, Morgan Freeman]便于前端解析genreVARCHAR(100)类型如 剧情,犯罪支持多类型ratingDECIMAL(2,1)评分0.0–10.0带一位小数poster_urlVARCHAR(500)海报图片 URL可为空summaryTEXT剧情简介支持富文本但本项目用纯文本对应 Java 实体Movie.javapublic class Movie { private Long id; NotBlank(message 电影名称不能为空) private String title; private String originalTitle; // 注意字段名与数据库 snake_case 不同靠 mybatis map-underscore-to-camel-case 映射 Min(1900) Max(2030) private Integer year; private String director; private String actors; // 存储 JSON 字符串业务层负责序列化/反序列化 private String genre; DecimalMin(0.0) DecimalMax(10.0) private BigDecimal rating; private String posterUrl; private String summary; // getter/setter 省略 }注意actors字段虽存 JSON 字符串但避免在 SQL 中直接LIKE %Tom%查询演员——这会导致全表扫描。真实项目应建actor表与movie_actor关联表此处为简化教学先用字符串存储后续章节会给出优化方案。2.4 编写 MyBatis Mapper 接口与 XML支持分页与模糊搜索MovieMapper.java接口定义基础操作Mapper public interface MovieMapper { ListMovie selectAll(); // 获取全部电影供首页轮播 ListMovie selectByGenre(Param(genre) String genre); // 按类型筛选 ListMovie search(Param(keyword) String keyword); // 全字段模糊搜索 Movie selectById(Param(id) Long id); // 查单部详情 int insert(Movie movie); // 新增电影 int update(Movie movie); // 更新信息 int deleteById(Param(id) Long id); // 下架 }mapper/MovieMapper.xml实现 SQL关键点if动态拼接、LIMIT分页、CONCAT多字段搜索?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.movie.mapper.MovieMapper resultMap idMovieResultMap typecom.example.movie.entity.Movie id propertyid columnid/ result propertytitle columntitle/ result propertyoriginalTitle columnoriginal_title/ result propertyyear columnyear/ result propertydirector columndirector/ result propertyactors columnactors/ result propertygenre columngenre/ result propertyrating columnrating/ result propertyposterUrl columnposter_url/ result propertysummary columnsummary/ /resultMap !-- 全字段模糊搜索标题、导演、类型均匹配 keyword -- select idsearch resultMapMovieResultMap SELECT * FROM movie WHERE CONCAT(title, , director, , genre) LIKE CONCAT(%, #{keyword}, %) ORDER BY rating DESC LIMIT 20 /select !-- 按类型筛选支持多个类型用逗号分隔如 剧情,爱情 -- select idselectByGenre resultMapMovieResultMap SELECT * FROM movie WHERE genre LIKE CONCAT(%, #{genre}, %) ORDER BY rating DESC /select /mapper提示CONCAT(title, , director, , genre)是简易全文搜索方案避免引入 ElasticsearchLIMIT 20防止搜索结果过多拖慢页面。生产环境应加索引ALTER TABLE movie ADD FULLTEXT(title, director, genre);并改用MATCH AGAINST。3. 实现电影网站前端页面Thymeleaf 模板与跨浏览器兼容处理3.1 主页index.html响应式布局与动态数据渲染Thymeleaf 模板位于src/main/resources/templates/index.html结构需兼顾 SEO 和移动端适配!DOCTYPE html html xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title电影网站 - 首页/title link relstylesheet th:href{/css/bootstrap.min.css} style .movie-card { height: 400px; overflow: hidden; } .movie-poster { width: 100%; height: 250px; object-fit: cover; } media (max-width: 768px) { .movie-card { height: auto; } } /style /head body div classcontainer mt-4 !-- 搜索栏 -- form th:action{/search} methodget classmb-4 div classinput-group input typetext namekeyword classform-control placeholder搜索电影名、导演或类型... required button classbtn btn-primary typesubmit搜索/button /div /form !-- 分类导航 -- div classrow mb-4 div classcol-auto th:eachgenre : ${genres} a th:href{/genre/{g}(g${genre})} classbtn btn-outline-secondary me-2 th:text${genre}剧情/a /div /div !-- 电影列表 -- div classrow div classcol-md-4 mb-4 th:eachmovie : ${movies} div classcard movie-card h-100 img th:src${movie.posterUrl} ?: /img/default-poster.jpg th:alt${movie.title} classmovie-poster card-img-top div classcard-body d-flex flex-column h5 classcard-title th:text${movie.title}肖申克的救赎/h5 p classcard-text text-muted th:text${movie.year} · ${movie.genre}1994 · 剧情,犯罪/p div classmt-auto span classbadge bg-warning text-dark th:text${movie.rating} /109.7/10/span a th:href{/detail/{id}(id${movie.id})} classbtn btn-sm btn-primary float-end查看详情/a /div /div /div /div /div /div script th:src{/js/bootstrap.bundle.min.js}/script /body /html注意th:src${movie.posterUrl} ?: /img/default-poster.jpg使用 Elvis 操作符提供默认海报避免null导致图片 404media查询确保在手机上卡片高度自适应解决“加载 web 视图时出错: error: could not register service worker: invalidstatee”这类因 CSS 未适配移动端引发的渲染异常。3.2 搜索与详情页URL 路由与参数传递规范MovieController.java处理请求Controller public class MovieController { Autowired private MovieService movieService; GetMapping(/) public String index(Model model) { model.addAttribute(movies, movieService.selectAll()); model.addAttribute(genres, Arrays.asList(剧情, 爱情, 动作, 科幻, 动画)); return index; } GetMapping(/search) public String search(RequestParam String keyword, Model model) { model.addAttribute(movies, movieService.search(keyword)); model.addAttribute(keyword, keyword); return search-result; } GetMapping(/genre/{genre}) public String byGenre(PathVariable String genre, Model model) { model.addAttribute(movies, movieService.selectByGenre(genre)); model.addAttribute(genre, genre); return genre-list; } GetMapping(/detail/{id}) public String detail(PathVariable Long id, Model model) { Movie movie movieService.selectById(id); if (movie null) { return redirect:/; // ID 不存在跳转首页 } model.addAttribute(movie, movie); return movie-detail; } }提示PathVariable直接绑定 URL 路径参数比RequestParam更符合 RESTful 风格return redirect:/避免空数据导致 500 错误提升健壮性。3.3 解决跨浏览器支持的关键 CSS 与 JS 兼容点为保障在 Chrome、Firefox、Edge 甚至旧版 Safari 正常显示需处理三处Flexbox 兼容d-flex类在 IE11 需加-ms-前缀但 Bootstrap 5 已移除 IE 支持故选用 Bootstrap 4.6.2仍支持 IE10link relstylesheet th:href{/css/bootstrap.min.css} !-- Bootstrap 4.6.2 CSS 文件已内置 autoprefixer 处理 --JSON 解析兼容前端若需解析actors字段如JSON.parse(movie.actors)IE11 不支持JSON对象需引入 polyfillscript th:src{/js/json-polyfill.min.js}/script !-- 该文件仅 2KB解决 IE11 JSON.parse undefined 问题 --CSS 变量降级避免使用--primary-color自定义属性改用传统 class/* 不推荐 */ :root { --primary-color: #007bff; } .btn-primary { background-color: var(--primary-color); } /* 推荐直接写死或用 Bootstrap 变量 */ .btn-primary { background-color: #007bff; }4. 后台管理功能实现增删改查与安全防护要点4.1 管理员登录与权限拦截基于 Session 的轻量级认证电影网站设计与实现中后台管理/admin/**必须与前台分离。采用HttpSession存储登录状态避免引入 Spring Security 增加复杂度Controller public class AdminController { PostMapping(/admin/login) public String login(RequestParam String username, RequestParam String password, HttpSession session, Model model) { if (admin.equals(username) 123456.equals(password)) { session.setAttribute(adminLoggedIn, true); return redirect:/admin/dashboard; } else { model.addAttribute(error, 用户名或密码错误); return admin-login; } } GetMapping(/admin/dashboard) public String dashboard(HttpSession session, Model model) { if (session.getAttribute(adminLoggedIn) null) { return redirect:/admin/login; } model.addAttribute(movieCount, movieService.countAll()); return admin-dashboard; } GetMapping(/admin/logout) public String logout(HttpSession session) { session.removeAttribute(adminLoggedIn); return redirect:/admin/login; } }对应admin-login.html表单form th:action{/admin/login} methodpost div classmb-3 label forusername classform-label用户名/label input typetext classform-control idusername nameusername required /div div classmb-3 label forpassword classform-label密码/label input typepassword classform-control idpassword namepassword required /div button typesubmit classbtn btn-primary登录/button div classtext-danger th:if${error} th:text${error}错误提示/div /form注意此方案仅适用于学习项目。生产环境必须用 BCrypt 加密密码、JWT Token 替代 Session、增加验证码防暴力破解。4.2 电影管理页面表单验证与文件上传处理admin-add-movie.html支持上传海报图片form th:action{/admin/movie/save} methodpost enctypemultipart/form-data div classmb-3 label classform-label电影名称/label input typetext classform-control nametitle required /div div classmb-3 label classform-label上映年份/label input typenumber classform-control nameyear min1900 max2030 required /div div classmb-3 label classform-label海报图片/label input typefile classform-control nameposter acceptimage/* /div button typesubmit classbtn btn-success保存电影/button /form后端AdminController.java处理上传PostMapping(/admin/movie/save) public String saveMovie( RequestParam String title, RequestParam Integer year, RequestParam(required false) MultipartFile poster, Model model) throws IOException { Movie movie new Movie(); movie.setTitle(title); movie.setYear(year); // 保存图片到 static/img/ 目录并存 URL 到数据库 if (!poster.isEmpty()) { String fileName System.currentTimeMillis() _ poster.getOriginalFilename(); Path uploadPath Paths.get(src/main/resources/static/img/, fileName); Files.createDirectories(uploadPath.getParent()); poster.transferTo(uploadPath); movie.setPosterUrl(/img/ fileName); } movieService.insert(movie); return redirect:/admin/dashboard; }提示MultipartFile是 Spring 封装的文件上传接口transferTo()安全写入磁盘static/img/路径使图片可通过http://localhost:8080/img/xxx.jpg直接访问无需额外 Controller。4.3 SQL 注入与 XSS 防护MyBatis 参数绑定与 Thymeleaf 自动转义电影网站源码若直接拼接 SQL 或输出未过滤的用户输入极易被攻击。本方案双重防护SQL 注入防护MyBatis 使用#{}占位符而非${}自动转义参数!-- 安全预编译参数 -- select idsearch resultTypeMovie SELECT * FROM movie WHERE title LIKE CONCAT(%, #{keyword}, %) /select !-- 危险字符串拼接易注入 -- select idsearchBad resultTypeMovie SELECT * FROM movie WHERE title LIKE %${keyword}% /selectXSS 防护Thymeleaf 默认对th:text、th:utext做 HTML 转义!-- 自动转义scriptalert(1)/script → lt;scriptgt;alert(1)lt;/scriptgt; -- p th:text${movie.summary}简介/p !-- 若需渲染 HTML如富文本显式用 th:utext但必须先过滤 -- div th:utext${safeSummary}安全简介/div注意movie.summary若来自用户输入必须在 Service 层用 Jsoup 清洗String safeSummary Jsoup.clean(rawSummary, Whitelist.basic());5. 本地部署与常见问题排错从启动失败到页面空白的全流程诊断5.1 Tomcat 启动失败的三大高频原因与修复命令当执行mvn spring-boot:run报错Web server failed to start按顺序排查现象原因诊断命令修复方式Failed to configure a DataSourceapplication.yml中spring.datasource.url格式错误或 MySQL 未启动telnet localhost 3306检查 MySQL 是否运行URL 末尾加?serverTimezoneAsia/Shanghaijava.lang.ClassNotFoundException: javax.servlet.FilterSpring Boot 2.7 默认移除javax.*包需降级或替换mvn dependency:tree | grep servlet在pom.xml添加spring-boot-starter-tomcat依赖Caused by: java.io.FileNotFoundException: class path resource [static/css/bootstrap.min.css]静态资源路径错误Thymeleaf 找不到 CSSls target/classes/static/css/确保src/main/resources/static/css/目录存在且bootstrap.min.css文件名无空格提示执行mvn clean compile清理旧 class再mvn spring-boot:run启动避免缓存干扰。5.2 页面 404 或空白检查 Thymeleaf 模板路径与控制器映射若访问http://localhost:8080/显示 Whitelabel Error Page检查 Controller 返回值return index对应templates/index.html路径必须完全一致验证模板语法打开index.html确认首行有html xmlns:thhttp://www.thymeleaf.org查看日志关键行启动日志中搜索Mapped确认MovieController.index()是否注册Mapped {[/], methods[GET]} onto public java.lang.String com.example.movie.controller.MovieController.index(org.springframework.ui.Model)若页面空白但无报错检查浏览器开发者工具F12→ Console 标签页常见错误Uncaught SyntaxError: Unexpected token JS 文件返回了 HTML如/js/bootstrap.js实际返回 404 页面检查src/main/resources/static/js/是否存在该文件Failed to load resource: net::ERR_CONNECTION_REFUSED前端请求的 API 地址错误如fetch(/api/movies)应改为/moviesSpring Boot 默认无/api前缀。5.3 数据库中文乱码终极解决方案MySQL 配置与连接参数双保险电影网站设计与实现中INSERT INTO movie (title) VALUES (阿凡达)存入乱码????需四步同步设置MySQL 服务端配置my.cnf[client] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci重启 MySQLsudo systemctl restart mysqlLinux或服务管理器重启重建数据库已有数据需导出DROP DATABASE movie_db; CREATE DATABASE movie_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;JDBC 连接 URL 强制指定application.ymlspring: datasource: url: jdbc:mysql://localhost:3306/movie_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai注意utf8mb4支持 Emoji 和生僻汉字如 “”utf8在 MySQL 中实际是utf8mb3不支持四字节 Unicode。5.4 性能优化技巧为电影网站添加 Redis 缓存热点数据当首页selectAll()每次请求都查库QPS 上升后数据库压力剧增。用 Redis 缓存 10 分钟内的电影列表添加依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency配置application.ymlspring: redis: host: localhost port: 6379在MovieService.java方法上加注解Cacheable(value movies, key #root.methodName, unless #result null) public ListMovie selectAll() { return movieMapper.selectAll(); }启用缓存在主类加EnableCaching。提示Cacheable会将方法返回值序列化存入 RedisKey 为movies:selectAll下次调用直接返回缓存绕过数据库。清除缓存用CacheEvict(value movies, allEntries true)如新增电影后调用。启动 Redis 服务macOSbrew services start redisWindows 用户下载 Redis Desktop Manager 连接localhost:6379查看缓存 Key。本文还有配套的精品资源点击获取
返回列表