Java新闻发布系统毕设指南:Spring Boot+MyBatis从架构到避坑

发布时间:2026/10/8 3:45:35
Java新闻发布系统毕设指南:Spring Boot+MyBatis从架构到避坑 简介基于Java的新闻发布及管理系统毕业设计资料包面向计算机专业毕业生及需要完成动态网页开发课题的初级开发者适用于基于Web的信息管理系统方向。系统以新闻的实时发布与管理为核心采用JSP动态页面、MySQL数据库与Tomcat服务器实现覆盖新闻类别管理、内容审核、前台展示与后台维护等关键环节。资源共45个文件包含完整Java源代码、可导入的SQL数据库脚本、Word版项目说明报告、PPT答辩演示文稿及40张系统运行截图压缩包总大小约35.49MB文档与代码分离存放查阅方便。目前已有231人学习下载。借助项目报告与答辩PPT可快速梳理需求分析、数据库设计和功能实现思路结合源代码可直接部署运行或二次扩展是一套结构完整、适合毕业设计参考与实训演练的实用方案。1. 基于Java的新闻发布及管理系统毕设里最稳的题目也是翻车最多的题目每年毕业季Java方向的毕设题目里总有一批「新闻发布及管理系统」。这个题听起来不惊艳但它把Web开发最核心的链路全包进去了用户登录与鉴权、新闻分类管理、内容增删改查、分页展示、图片上传外加一套关系型数据库设计。适合谁适合想用一套不花哨但完整的技术栈把Servlet、Spring、MyBatis、MySQL这些基础能力亲手串起来的人。下面我按自己带项目时的常用方案把技术选型、核心实现、常见坑和答辩加分项拆开讲。照着复现你能少走至少一周弯路已经写完代码的也能从里面找到几个能让答辩现场更稳的补救点。2. 系统架构与技术选型三层架构和Spring Boot让新闻系统不再难维护2.1 三层架构表现层、业务层、持久层在新闻系统里怎么分工新闻发布系统的原始需求并不复杂管理员登录后台编辑发布新闻访客在前台浏览。但直接在一个JSP页面里写数据库操作、业务判断和HTML输出前期确实快后期改需求会让你想删库跑路。最常见也最稳的方案是三层架构。表现层ViewJSP页面配合SpringMVC的Controller负责接收请求参数、调用业务层、渲染响应页面。业务层Service处理新闻发布的具体规则比如标题不能为空、发布前要校验分类是否存在、点击量自增的逻辑放在哪。持久层Dao/Mapper用MyBatis操作数据库完成增删改查不掺任何业务判断。为什么新闻系统特别适合三层因为它的业务边界很清晰。前台展示和后台管理共用同一个Service只是Controller和视图不同——访客看到的是新闻列表页管理员看到的是带编辑、删除按钮的管理页。如果业务逻辑写在Servlet里两组Servlet就得各写一遍校验和事务代码改一个字段要动两个地方这就是教科书里说的耦合。我在评审毕设时见过不少Service层写成空壳的项目所有逻辑堆在Controller里被问到“业务层到底做了什么”就冷场。Service层至少要干三件事参数合法性校验、事务边界控制、把多个持久层操作编排成一个完整业务动作。后面写代码时我会按这个标准来你在设计类图时也建议按这个思路画。2.2 技术栈对比SSH、SSM、Spring Boot哪个更适合毕设技术栈选型直接决定你接下来两个月是写业务代码还是写配置文件。我把三个常见方案放在一起对比方案组成优点缺点适合场景SSHStruts2 Spring Hibernate教材多、网上资料老配置繁琐Struts2漏洞较多学校明文指定时再用SSMSpringMVC Spring MyBatis轻量、SQL可控、经典XML配置仍然不少想练传统配置流程Spring Boot MyBatis PlusSpring Boot MyBatis Plus自动配置、起步快、CRUD代码量少需要理解自动配置原理时间紧、想加亮点如果学校没限定框架我一般推荐Spring Boot MyBatis Plus。原因很实在Spring Boot帮你省掉SpringMVC那堆XML装配MyBatis Plus对单表CRUD有现成方法新闻系统核心表结构并不复杂单表操作用MyBatis Plus能让代码量减少一半以上。但要注意如果答辩老师是传统SSH派可能会追一句“Spring Boot和SSM什么关系”。我会在PPT里单独放一页「Spring Boot的底层仍然是Spring MVC Spring」写明自动配置替代了手工装配这一页基本必被问到提前准备好能避免现场尴尬。Spring Boot的基础配置如下spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/news_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml type-aliases-package: com.example.news.entity configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这里有两个参数特别容易出错。第一个是url里的serverTimezoneMySQL 8.x默认时区与驱动不一致不配会直接报时区异常。第二个是useUnicode和characterEncodingtrue少了它中文进数据库就变成问号。这两类问题后面避坑章节还会展开。2.3 数据库建表news、category、user三张核心表的设计与SQL新闻发布系统的核心表就三张用户表、分类表、新闻表。用户表存管理员账号和角色分类表存新闻栏目国内、国际、体育这类新闻表存标题、正文、封面图、发布人、发布时间、点击量、状态。新闻表是最容易设计过度的表。有的毕设为了体现水平拆成新闻主表加新闻详情表再做全文索引结果答辩时连JOIN都讲不顺。演示级别的访问量单表完全够用。如果确实想加分多加created_at、updated_at两个时间字段再给publish_time建个索引这就已经是合理的设计取舍了。CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT DEFAULT 1 COMMENT 1管理员, 2编辑, status TINYINT DEFAULT 1 COMMENT 1正常, 0禁用, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, sort INT DEFAULT 0 COMMENT 排序值越小越靠前 ); CREATE TABLE news ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, title VARCHAR(200) NOT NULL, content TEXT, cover_image VARCHAR(255), author_id INT NOT NULL, view_count INT DEFAULT 0, status TINYINT DEFAULT 0 COMMENT 0草稿, 1已发布, publish_time DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_publish_time (publish_time), CONSTRAINT fk_news_category FOREIGN KEY (category_id) REFERENCES category(id), CONSTRAINT fk_news_author FOREIGN KEY (author_id) REFERENCES user(id) );有两个隐藏点要说清楚。第一content用TEXT类型而不是VARCHAR新闻正文长度极可能超过255字符这是数据库设计基础题。第二外键要建吗我建议建。虽然外键影响写入性能但毕设项目里外键体现的是你懂参照完整性回答“为什么建外键”时说“防止删除分类时留下游离的新闻”就够了。如果你正在写项目报告把这张图的数据字典做到三张表以上再加一张评论表或友情链接表工作量可控目录也更饱满。热门检索里经常提到“数据库增删改查”这三张表正好把增删改查的边界划分得很清楚对user和category主要是增删改对news四类操作都有。3. 核心功能实现登录鉴权、新闻分页、图片上传的完整链路3.1 登录鉴权用Filter统一拦截比在每个Servlet里校验更省事写登录功能之前先写一个登录校验的过滤器把整个系统的访问规则定下来。很多新手第一步就去写登录逻辑结果每个页面都要复制一份Session判断重复代码到处都是。public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) res; String path request.getRequestURI(); // 放行登录请求、静态资源和公开页面 if (path.endsWith(/login) || path.contains(/static/) || path.endsWith(.css) || path.endsWith(.js) || path.endsWith(.jpg) || path.endsWith(.png)) { chain.doFilter(request, response); return; } // 校验Session中是否存在已登录用户 Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login); return; } chain.doFilter(request, response); } }逻辑说明所有请求先走到这个过滤器登录接口、CSS、JS、图片和静态资源直接放行其他请求统一检查Session。Session里没有登录用户就重定向到登录页。这种方式把权限判断集中在一处后续新增页面不需要再写重复校验。Spring Boot里需要注册这个过滤器我一般这样写Configuration public class WebConfig { Bean public FilterRegistrationBeanLoginFilter loginFilter() { FilterRegistrationBeanLoginFilter registration new FilterRegistrationBean(); registration.setFilter(new LoginFilter()); registration.addUrlPatterns(/admin/*); return registration; } }关键点是只对/admin/*路径拦截。前台展示页面全部放行否则访客连新闻列表都看不了。有人图省事拦截全部路径结果登录页本身被拦截形成死循环这是最常见的误配。Filter里吃完异常后要记得chain.doFilter否则请求链断掉页面会白屏。3.2 新闻分页与条件查询PageHelper 动态SQL的配合与三个注意点后台新闻列表必须分页这是必考功能。我直接用PageHelper插件配合MyBatis动态SQL实现标题搜索、分类筛选、状态筛选和分页的组合查询。select idselectPageList resultTypecom.example.news.entity.News SELECT n.id, n.title, n.category_id, n.status, n.publish_time, c.name AS category_name FROM news n LEFT JOIN category c ON n.category_id c.id where if testtitle ! null and title ! AND n.title LIKE CONCAT(%, #{title}, %) /if if testcategoryId ! null AND n.category_id #{categoryId} /if if teststatus ! null AND n.status #{status} /if /where ORDER BY n.publish_time DESC /selectService层的分页调用只需要两行PageHelper.startPage(pageNum, pageSize); ListNews newsList newsMapper.selectPageList(condition);逻辑说明PageHelper的startPage方法会拦截紧接着的第一条Mapper查询自动追加LIMIT语句同时自动生成COUNT查询。后面拿到的是分页后的数据再封装一个分页结果对象把total、list、pageNum、pages一起返回给前端。这里有两个使用PageHelper的硬性规定。第一startPage之后必须紧跟第一条查询语句中间不能穿插任何其他SQL操作否则分页参数会被错误消费。第二pageNum从1开始pageSize常见值是5或10前端表格底部要有总条数和总页数。如果翻页之后发现数据重复或缺失优先怀疑PageHelper在多数据源或自定义拦截器场景下失效了检查拦截器的顺序配置。动态SQL里 标签会自动去掉多余的AND或OR这是MyBatis自带的能力不需要手动拼WHERE。如果你写的是纯字符串SQL拼接一定要防SQL注入不要让用户输入直接拼进SQL用#{}占位符而不是${}。这句话答辩时经常被追问提前准备一下比较好。3.3 图片上传物理路径和访问路径分开存图片就不会刷新就404新闻封面图上传是另一个高频功能也是最能暴露基本功的点。原理是前端用multipart/form-data提交文件后端把文件写入磁盘某个目录再把文件对应的访问路径保存到数据库。这里最大的坑是物理路径和访问路径混为一谈。PostMapping(/upload) public String upload(RequestParam(file) MultipartFile file, HttpServletRequest request) { if (file.isEmpty()) { return 上传失败文件为空; } // 生成唯一文件名避免中文文件名和重复名导致乱码、覆盖 String original file.getOriginalFilename(); String ext original.substring(original.lastIndexOf(.)); String fileName System.currentTimeMillis() _ UUID.randomUUID().toString().substring(0, 8) ext; // 保存到应用部署目录下的 uploads 文件夹 String realPath request.getSession().getServletContext().getRealPath(/uploads); File dir new File(realPath); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, fileName)); // 数据库只保存访问路径 String visitPath /uploads/ fileName; news.setCoverImage(visitPath); newsMapper.updateById(news); return 上传成功; }这段代码最关键的就是最后的visitPath。数据库里永远只存/uploads/xxx.jpg这种访问路径不存绝对物理路径。因为同一套代码在Windows和Linux服务器上部署绝对路径完全不同。你把本地D盘路径存进数据库换一台机器就全是404这就是典型的“图片上传好了一刷新就没”的根源。另外Spring Boot默认上传大小限制是1MB上传稍大的封面图会直接报MaxUploadSizeExceededException。在application里调大限制spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB这两个参数对毕设来说足够了。如果要进一步体现水平可以用日期分目录存储比如/uploads/2025/04/文件名.jpg这样一年后的文件管理不会全堆在一个目录里。答辩时主动讲出这一点老师会觉得你想到了生产环境的问题。4. 答辩避坑5个让评委皱眉的常见问题和排查方案4.1 数据库连接信息硬编码在代码里评委一句话问倒现象数据库地址、用户名、密码直接写在某个常量类或JDBC连接代码里。评委问“换一台机器部署怎么办”学生沉默氛围尴尬。原因急于写业务忽略了配置与代码分离的基本工程习惯。这是最常见的低级问题但也是每次答辩都会出现的问题。解决把数据源配置全部挪到application.yml或独立的jdbc.properties里代码只负责读取配置。Spring Boot天然支持这一步不需要额外引入依赖。答辩时明确说“修改数据库连接只改配置文件不需要改代码”这本身就是一段标准的加分回答。更进一步敏感信息可以用环境变量占位password: ${DB_PASSWORD}说明部署时用环境变量覆盖这就是生产环境常见做法。4.2 中文乱码从JSP、Filter到数据库连接串逐个排查现象新闻内容从页面提交到数据库后全是问号或者页面显示新闻标题变成乱码。原因三层里任何一处编码不一致都会出问题。JSP页面没声明UTF-8、Filter没设置请求编码、JDBC连接串缺了characterEncoding、MySQL表本身是latin1。解决按顺序排查。第一步JSP页面文件头和响应头都声明UTF-8。第二步在登录过滤器或独立编码过滤器里设置response.setCharacterEncoding(UTF-8)和request.setCharacterEncoding(UTF-8)。第三步确认数据库连接串带了characterEncodingutf-8。第四步用SHOW VARIABLES LIKE character%;确认MySQL的character_set_server是utf8mb4。排查时从最后一步开始先看数据库里存的到底是什么再回溯页面层。如果库里已经是乱码源头不在数据库在写入链路如果库是好的、页面乱码问题在读出链路这是常见的排查思路。4.3 分页翻页丢条件第二页开始就不是你想要的数据现象第一页做了标题搜索或分类筛选数据正确点击第二页后条件全丢了分页结果包含所有新闻。原因翻页的URL只带了pageNum参数没有把title、categoryId、status这些查询条件拼接进去后端收到空条件自然执行全量查询。解决分页标签生成时把当前查询条件一起带上。常见做法是用一个Map或Model对象在渲染页面时回显所有条件生成的分页链接形如/admin/news/list?pageNum2titlejavacategoryId3。后端Controller从请求参数里逐个接收条件再传给分页Service。这里还要注意一个细节空字符串和null要区分处理否则筛选条件传空字符串会干扰SQL。动态SQL里用 来排除空串就能避开。4.4 图片上传成功但刷新就404虚拟目录和真实目录分不清现象上传封面图后页面正常显示点击刷新按钮图片就裂了或者重启应用后所有上传图片全部丢失。原因文件被写到了IDEA启动的虚拟目录里或者写到了Tomcat的临时工作目录。应用重启或部署迁移后这些目录被清理数据库里的访问路径就指向了不存在的文件。解决数据库永远存访问路径文件写到固定真实目录。如果用的是外置Tomcat部署把uploads目录放在webapp下与WEB-INF同级如果用的是Spring Boot内嵌Tomcat不要写入临时目录建议配置一个静态资源映射把本地固定磁盘目录映射到/uploads访问路径。答辩时讲到“本地存储只能用于演示生产环境要用对象存储”这是一个高价值的主动补充能体现你对存储方案的横向了解。4.5 答辩现场MySQL连接失败端口占用和字符集是最常见元凶现象演示时页面白屏或直接报Communications link failure数据库客户端也连不上。原因最典型的情况是3306端口被另一个进程占用导致MySQL服务其实没起来或者MySQL服务确实起来了但密码字符集配置变了驱动连不上还有一种情况是之前用管理员身份启动过服务权限残留。解决答辩前一晚专门做一次“冷启动演练”完全关闭IDEA和所有Java进程从命令行启动MySQL服务用mysql -uroot -p验证能登录再用浏览器走一遍前台、后台主要流程。端口占用排查用netstat -ano | findstr 3306Windows或lsof -i:3306macOS/Linux找到占用进程杀掉。连接串里的localhost和127.0.0.1交替备用排除本机解析问题。这一条是血泪经验毕业答辩翻车最多的就是数据库没有之一。5. 从「能跑」到「能答辩」三个加分项和一套验收清单5.1 AOP操作日志给评审一个「系统可追溯」的加分项很多新闻发布系统连用户操作日志都没有管理员发布或删除新闻后无从追溯。用Spring AOP给后台管理接口加日志切面成本很低但答辩谈资很足。我常用注解加切面的做法自定义一个OpLog注解标记需要记录的方法切面里在方法执行前后记录操作人、操作类型、耗时、IP。Aspect Component public class OpLogAspect { Around(annotation(opLog)) public Object record(ProceedingJoinPoint pjp, OpLog opLog) throws Throwable { long start System.currentTimeMillis(); String method pjp.getSignature().getName(); try { Object result pjp.proceed(); // 记录成功日志操作人、方法名、耗时 return result; } catch (Exception e) { // 记录失败日志异常信息、方法名 throw e; } } }逻辑说明AOP把通用日志逻辑抽离出业务代码Controller只写新闻CRUD日志由切面统一处理。答辩时解释“为什么要用AOP”——因为日志是国家要求的审计需求与业务逻辑正交切面编程正好解耦。这个点比任何CRUD代码都更有讨论空间。5.2 后端参数校验不能只靠前端Service层要兜底前端表单可以限制标题必填、长度不超过200但请求可以被直接构造绕过前端校验。后端不在Service层做参数校验脏数据就会进数据库。Spring Boot里用javax.validation注解加Valid是最常见做法。在新闻实体类的title字段上加NotBlank和Length(max 200)在Controller参数上加Valid框架自动拦截非法请求并返回校验错误。这一点讲出来评委不会再觉得你是只会写增删改查的“新手”。类似的原理问题本质上也是java面试题里的常客提前理顺后面积累的知识可以复用。5.3 验收清单从打包部署到浏览器全流程走一遍答辩前一晚按清单验收第一步用Maven执行clean package构建出war或jar包确认构建无报错。第二步把构建产物部署到干净的Tomcat目录或用java -jar启动Spring Boot应用。第三步新开浏览器隐身窗口按顺序走登录、发布新闻、上传封面图、前台查看、翻页、搜索、退出登录。每一项流程都要截图存一份PPT演示时如果现场网络或环境出问题截图能兜底。第四步重启应用后再次访问图片和新闻列表确认文件没有丢失。我自己的习惯是每次做这类毕设评审都会先问一句话把你的项目删掉所有编译产物只留源码、配置和SQL脚本给我一台干净机器你能不能独立部署起来能答上来的基本都能过。这套流程走一遍你心里对项目是否有底气比演示当天才知道要踏实得多。数据库同步软件、备份脚本这些东西不是必选项但如果你把news表的导出脚本和备份说明放进项目报告附录会让整个项目显得更完整。最后说一句个人的教训新闻发布系统这类毕设难点从来不在功能多而在边界清不清楚——哪些路径放行、哪些数据需要校验、哪些时间字段要维护、哪些存储路径不能写死。把这些边界问题解决了答辩时你讲起来就是一套完整的设计思路没解决演示画面再漂亮也经不住追问。希望帮到你。本文还有配套的精品资源点击获取