简介:基于Java+SSM+MySql开发的新闻发布及管理系统毕业设计资源,面向计算机专业学生、SSM框架开发者,可用于课程设计、毕业设计或项目实战练习。系统前台涵盖用户注册、登录、找回密码与新闻评论,后台包含管理员账号管理、系统公告、新闻栏目、新闻资讯、注册用户、友情链接和新闻评论管理等模块,完整呈现内容管理系统的典型业务链。压缩包约115.8MB,内含可运行源码、数据库脚本、毕业论文、答辩PPT及视频教程:源码便于导入IDE二次开发,数据库脚本可快速搭建环境,论文与答辩材料辅助整理设计思路,视频教程降低上手门槛。已有64人学习,资源整体覆盖从环境搭建、功能实现到论文撰写与答辩展示的全过程,实用性和参考价值较高。
1. 拿到SSM新闻发布及管理系统:先想清楚这三件事再动手
基于Java+SSM+MySQL的新闻发布及管理系统,是毕业设计里出现频率极高的一个Java Web完整案例:Spring管理对象、SpringMVC处理请求、MyBatis操作MySQL,配套完整源码、数据库脚本、毕业论文、答辩PPT和视频教程。对时间紧、又需要交一份能讲清楚的项目的人来说,这套方案最大的价值不是“代码能不能跑”,而是“你能不能在一周内把别人的项目变成自己的”。
我见过太多人拿到完整源码后第一件事是看视频教程,跟着配置环境,结果卡在数据库连接和依赖上。真正反直觉的结论是:完整源码的交付物里,最耗时间的不是写代码,而是环境适配、数据库脚本导入和把请求链路读明白。本篇不替你把代码重写一遍,只讲最可靠的落地路径:怎么跑起来、怎么读懂SSM分工、怎么改功能、踩坑后怎么排错,以及答辩前要怎么验证自己真的掌握了这套系统。
2. 把SSM+MySQL的新闻系统跑起来:JDK、Tomcat、数据库连接三步到位
很多毕业生拿到这套资源后,第一反应是直接双击打开源码。常见做法是先别碰代码,先把运行环境对齐,否则后面改一个错一个,最后分不清是代码问题还是环境问题。这一章的目标只有一个:让你在半小时内看到新闻列表页面出现在浏览器里。
2.1 环境版本怎么配:JDK 1.8、Tomcat 8.5、MySQL 5.7 是最稳的组合
这套系统在大多数毕业设计里是按老项目写的,太新的环境反而容易翻车。我一般建议的默认组合是:JDK 1.8、Maven 3.6.x、Tomcat 8.5、MySQL 5.7。如果你手里装的是MySQL 8.0,也能跑,但后面第5章会专门讲MySQL 8的密码插件和SSL连接问题。建议先在命令行确认一下:
java -version mvn -version mysql --version只要这三个命令都能输出版本号,环境基本就算立住了。很多人卡在java环境变量配置上,看视频教程说配JAVA_HOME和PATH,配完还是提示“不是内部或外部命令”,常见原因是新开了一个命令行窗口导致环境变量没刷新,或者JAVA_HOME写到了jre目录而不是jdk目录。
关于Tomcat,不用刻意追求最新版。Tomcat 9之后默认Servlet版本高,老项目里的web.xml头信息如果不匹配,启动会直接报错。Tomcat 8.5对Spring 4.x、SpringMVC 4.x和Servlet 3.1的兼容性最好,这就是为什么网上绝大多数SSM毕设源码建议配8.5。
数据库安装方面,Windows下按mysql安装教程走默认端口3306即可;Linux下用apt或yum装完以后,记得先执行systemctl start mysqld再谈连接。安装完后要重点确认端口没被占用,root密码是自己能记住的。有一个土办法很管用:先装好Navicat for MySQL或MySQL Workbench,能在图形客户端里连上数据库,再去连项目,这样可以把“项目配置问题”和“MySQL本身问题”立刻分开。
2.2 jdbc.properties里的三个必改点:驱动、URL、账号密码
SSM项目一般都有一个数据库配置文件,通常在src/main/resources目录下,名字可能叫jdbc.properties、db.properties或者application.properties。要改的就是三个地方:驱动类、数据库URL、账号密码。下面这份配置是这类新闻系统的典型写法:
# src/main/resources/jdbc.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/news?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456jdbc.driver这一项要和你用的MySQL驱动版本对应。MySQL 5.7以前常用com.mysql.jdbc.Driver;MySQL 8.0的驱动包要求写成com.mysql.cj.jdbc.Driver。如果项目lib里带的还是老驱动,强行写新驱动类会报ClassNotFoundException。最稳妥的做法是检查pom.xml里mysql-connector-java的版本,再决定driver写哪一个。
jdbc.url里有几个参数属于“不写必踩坑”系列:useUnicode=true和characterEncoding=utf8决定中文能不能正常写入读出;useSSL=false用来关掉SSL握手,MySQL 8的SSL连接错误在项目启动时非常常见;serverTimezone=Asia/Shanghai是MySQL 8.0的时区要求,不写会报server time zone value。这里要特别提醒:localhost改成127.0.0.1能绕开本机socket解析问题。有些系统上localhost会走unix socket,应用连接时反而报错。
账号密码不用多解释,只要和MySQL里实际存在的用户一致即可。如果你想独立测试这个配置是否正确,可以在Navicat for MySQL里新建连接,用同一组root和密码去连,连得上说明问题在项目侧,连不上说明要回去改MySQL。
2.3 最小启动路径:建库、导SQL脚本、打war包扔进Tomcat
数据库脚本通常是一个.sql文件,里面包含建库、建表和测试数据。拿到手以后,不要双击打开看,直接用命令行导入。这里以脚本文件名news.sql为例:
# 1. 创建数据库,字符集必须是utf8 mysql -uroot -p -e "create database news default character set utf8;" # 2. 把脚本导入到news库 mysql -uroot -p news < news.sql # 3. 用Maven打包,跳过测试 mvn clean package -DskipTests # 4. 把war包复制到Tomcat的webapps目录 cp target/news.war $TOMCAT_HOME/webapps/ # 5. 启动Tomcat $TOMCAT_HOME/bin/startup.sh每条命令的意图很简单:建库时指定default character set utf8,是因为脚本里的建表语句如果没写字符集,数据库默认字符集就会接管,很多中文乱码问题在这一步就埋下了。导入SQL脚本时用<重定向,比在Navicat里手动执行更不易漏语句;如果你更习惯图形界面,用Navicat或MySQL Workbench运行整个脚本也可以,但要注意选择正确的数据库再执行。
Maven打包这一步,很多初次接触Maven的人会卡在下载依赖上。如果网络不好,IDEA的Maven面板里一直显示下载中,可以在settings.xml里换阿里云镜像,这是老生常谈,但确实能解决大部分“pom.xml飘红”的问题。打包成功后,target目录下会出现news.war,复制到Tomcat的webapps目录后启动Tomcat,它会自动解压war包。
Windows环境把startup.sh换成startup.bat。启动完成后,浏览器访问http://localhost:8080/news/,看到首页新闻列表就算跑通了。如果404,不要急,去看Tomcat的logs/catalina.out,第5章会专门讲端口和404的排查。如果你是在IDEA里开发,常见做法是不打war包,直接把项目配置成Tomcat Server的Artifact来部署,效果一样,但打包方式更接近“交付”场景。
3. 读懂SSM框架分工与新闻系统核心表结构:一条新闻列表的请求路线
跑通项目只是开始,答辩时老师大概率会问“SSM三个框架分别干了什么”。这一章把新闻发布及管理系统拆成三层来看:框架分工、表结构设计、一次请求的完整路线。读完以后,你就能把代码和论文里的架构图对应上。
3.1 SSM三兄弟:Spring管对象,SpringMVC管请求,MyBatis管SQL
SSM框架是三个框架的合称:Spring、SpringMVC、MyBatis。很多java面试八股文里都会问三兄弟的分工,放在这个新闻系统里其实很好解释。
Spring是容器,负责创建和管理对象。比如NewsController、NewsService、NewsMapper这些都叫Bean,Spring通过扫描包把它们初始化并装配在一起,不在代码里到处new。SpringMVC是Web层,负责接收浏览器发来的URL请求,把它分发给Controller里的某个方法,再决定返回哪个JSP页面。MyBatis是持久层框架,把Java方法映射成SQL语句,负责和MySQL对话。
打开项目里的springmvc.xml,你会看到类似这样两行核心配置:
<!-- 开启SpringMVC注解驱动,Controller里的@RequestMapping才能生效 --> <mvc:annotation-driven/> <!-- 让Spring扫描controller包,把NewsController等类注册成Bean --> <context:component-scan base-package="com.news.controller"/>mvc:annotation-driven/ 是最容易被忽略的一行。没有它,@RequestMapping、@ResponseBody这些注解都不会被解析,项目启动可能不报错,但访问任何URL都404。component-scan里的base-package必须指向你实际的controller包名,如果项目里的包名是com.news.controller,这里写成com.news,扫进去的范围更大,一般也不会错,但指向明确更容易排查。
3.2 三张核心表:news、category、user怎么关联
新闻发布及管理系统的表一般不止三张,但核心业务一定绕不开这三张:新闻表、栏目表、用户表。新闻属于某个栏目,新闻由某个用户发布,这就是最基本的数据关系。典型建表语句如下:
CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE news ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, content TEXT, category_id INT, author_id INT, publish_time DATETIME, KEY idx_category (category_id), CONSTRAINT fk_news_category FOREIGN KEY (category_id) REFERENCES category(id), CONSTRAINT fk_news_author FOREIGN KEY (author_id) REFERENCES user(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;news表里的category_id指向category表的id,author_id指向user表的id,这就是外键逻辑。用外键约束的好处是数据不容易出现“孤儿记录”,比如删除一个栏目时,如果下面还有新闻,数据库会拒绝执行,这在管理类系统里是合理的保护。表引擎选InnoDB,是因为它支持外键和事务;MyISAM在大数据量查询时快,但毕设管理系统用不到那个量级。
给category_id建一个普通索引KEY idx_category是有意的。新闻列表最常见的查询是按栏目筛选,索引能让这类查询少扫很多行。publish_time字段建议也建索引,因为“最新新闻”的排序全靠它,不加索引数据量到几万条后Order By会明显变慢。注意:实际项目里的表名和字段可能略有出入,比如用户表可能叫sys_user,栏目表可能叫news_type,理解关系比记表名更重要。
3.3 一条新闻列表的请求链路:从地址栏到JSP页面
理解了框架分工和表结构,我们再走一遍请求链路。用户在浏览器输入http://localhost:8080/news/news/list,Tomcat把请求交给DispatcherServlet,DispatcherServlet查到@Controller里的@RequestMapping("/news")和@RequestMapping("/list"),最终定位到NewsController里的list方法。代码长这样:
@Controller @RequestMapping("/news") public class NewsController { @Autowired private NewsService newsService; // 访问 /news/list 时进入这个方法 @RequestMapping("/list") public String list(Model model) { List<News> newsList = newsService.getLatestNews(); // 把数据放进Model,JSP里用EL表达式 ${newsList} 取 model.addAttribute("newsList", newsList); // 返回视图名,由视图解析器拼成 /WEB-INF/views/news/list.jsp return "news/list"; } }Controller本身很薄,只做三件事:接收参数、调用Service、把结果放到Model里返回视图名。它不直接写SQL,这是SSM分层的一个约定。NewsService的实现类里通过@Autowired注入了NewsMapper,调用Mapper接口方法,Mapper XML里才是真正的SQL:
@Service public class NewsServiceImpl implements NewsService { @Autowired private NewsMapper newsMapper; @Override public List<News> getLatestNews() { return newsMapper.selectLatestNews(); } }<select id="selectLatestNews" resultType="com.news.entity.News"> SELECT id, title, content, category_id, author_id, publish_time FROM news ORDER BY publish_time DESC LIMIT 10 </select>select标签里的id要和NewsMapper接口里的方法名一致,resultType要写实体类的全限定名。ORDER BY publish_time DESC就是利用MySQL排序把最新新闻放前面,LIMIT 10是只取前10条。MyBatis执行完查询后,把每行数据映射成News实体对象,装进List返回给Service,再回到Controller。Controller拿到数据放进Model,视图解析器把返回的"news/list"拼成/WEB-INF/views/news/list.jsp,JSP用EL表达式${newsList}循环输出新闻标题。整个链路到此结束。
这一条线就是SSM框架最精髓的地方:Controller不碰SQL,Mapper不碰页面,Service在中间做业务组装。答辩时能把这个链路画在纸上,比背十道java面试题管用。
4. 改造新闻发布功能:分页、搜索、排序与权限拦截
跑通和读懂之后,你大概率会想加一点自己的功能。新闻发布及管理系统最常见的改造方向就四个:分页、搜索、排序、后台权限拦截。它们难度适中,既不会动不了核心架构,又能在答辩时证明你“改过代码”。
4.1 给新闻列表加分页:PageHelper的用法和手写LIMIT的后路
新闻系统刚导入时,Mapper里很可能是LIMIT 10这种写死条数的SQL。要改成真正的分页,有两种做法。第一种是用PageHelper分页插件,这是SSM项目里最常用的答案。
在pom.xml加PageHelper依赖:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.2</version> </dependency>然后在mybatis-config.xml里注册插件:
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="helperDialect" value="mysql"/> </plugin> </plugins>Controller里的用法是:
PageHelper.startPage(pageNum, pageSize); List<News> newsList = newsService.getLatestNews(); PageInfo<News> pageInfo = new PageInfo<>(newsList);startPage后面必须紧跟要分页的查询语句,中间不能插入其他查询,否则分页会作用到错误的SQL上。pageNum从1开始,pageSize一般设5到10。PageInfo里封装了总条数、总页数、当前页、是否还有下一页等,前端分页条直接取这些字段即可,不用自己算。
不过PageHelper和旧版MyBatis的版本冲突是常见翻车点。如果你不想为版本折腾,第二种做法是手写LIMIT分页,这招在答辩时反而显得扎实:
<select id="selectNewsByPage" resultType="com.news.entity.News"> SELECT id, title, content, publish_time FROM news ORDER BY publish_time DESC LIMIT #{offset}, #{pageSize} </select>int offset = (pageNum - 1) * pageSize; List<News> newsList = newsMapper.selectNewsByPage(offset, pageSize);LIMIT后面第一个参数是偏移量,第二个是每页条数。第1页是offset=0,第2页是offset=pageSize,公式就是(pageNum-1)*pageSize。这种写法不依赖任何插件,逻辑透明,排查起来容易。我一般会建议毕设里优先用手写LIMIT,虽然代码多几行,但避免了两个框架版本打架的风险。
4.2 加一个关键词搜索:Mapper动态SQL和Controller入参
新闻管理系统加搜索功能是很自然的诉求。搜索的SQL核心是LIKE模糊匹配,但怎么写LIKE有讲究。直接在Mapper XML里这样写:
<select id="searchNews" resultType="com.news.entity.News"> SELECT id, title, content, publish_time FROM news WHERE title LIKE CONCAT('%', #{keyword}, '%') ORDER BY publish_time DESC </select>这里有一个典型误用:有人会写成WHERE title LIKE '%${keyword}%'。千万不要这么干,${}是字符串拼接,用户输入什么就直接拼进SQL,注入风险很大;#{}是预编译参数,MyBatis会先做参数绑定再执行,keyword里再有引号也不会破坏SQL结构。CONCAT('%', #{keyword}, '%')是在SQL里拼接模糊匹配的两端百分号,比在Java代码里拼好再传进去更安全。
Controller里接收参数时要考虑一个边界情况:用户打开搜索页但没输入关键词就提交了。如果keyword直接空着,LIKE '%'会匹配全部数据,看起来没问题,但如果keyword是null,SQL条件就变成LIKE '%%',也能查到全部。更合理的是在Controller里加一个required=false:
@RequestMapping("/search") public String search(@RequestParam(required = false) String keyword, Model model) { if (keyword == null || keyword.trim().isEmpty()) { model.addAttribute("newsList", newsService.getLatestNews()); } else { model.addAttribute("newsList", newsService.searchNews(keyword)); } return "news/list"; }关键词为空时直接走默认新闻列表,非空才走搜索。这样既不让用户看到一张空页,也避免空指针这种低级错误。如果你想升级,还可以把搜索条件从title扩展到content,用title LIKE或content LIKE的OR关系,但内容字段一般较长,多个LIKE会让查询变慢,毕设里保持标题搜索就够了。
4.3 后台权限拦截:把未登录用户挡在发布页面之外
新闻发布及管理系统一般分前台和后台。前台展示新闻不需要登录,后台发布、编辑、删除必须登录后才能操作。很多毕业设计里只在页面上用vue或jQuery判断有没有登录,这只能挡住普通用户,拦不住手动输URL的人。真正要挡入的是SpringMVC拦截器。
写一个登录拦截器:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从Session里取登录用户,这里是全局一套,没有则未登录 if (request.getSession().getAttribute("loginUser") == null) { response.sendRedirect(request.getContextPath() + "/login"); return false; } return true; } }然后在springmvc.xml里注册拦截路径:
<mvc:interceptors> <mvc:interceptor> <!-- 拦后台所有页面 --> <mvc:mapping path="/admin/**"/> <!-- 登录页本身不拦截,否则永远跳不进去 --> <mvc:exclude-mapping path="/admin/login"/> <bean class="com.news.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors><mvc:mapping path="/admin/**">的意思是拦截所有以/admin/开头的URL。排在后面的exclude-mapping优先级更高,所以/admin/login能正常放行。这里要注意:拦截的是URL路径,不是页面上的按钮,即使后端接口被绕过前端直接调用,也会被拦截器挡回去。这是把安全做到后端的关键一步。
用拦截器而不是Filter的原因是:拦截器能拿到Handler对象,能区分静态资源和Controller,处理更精细;但它只拦截SpringMVC的DispatcherServlet转发的请求。如果你在web.xml里把DispatcherServlet的url-pattern配成了*.do,那么/admin/login这种不带.do的URL根本不会进DispatcherServlet,拦截器不会生效。这一点和第3章讲的DispatcherServlet是联动的,排查时先看web.xml再找拦截器配置。
5. 避坑排查:MySQL 8连接、中文乱码、端口占用与404
这一章的每一条都是这套SSM新闻系统最常翻车的点。先说结论:遇到问题先看日志,不要凭感觉改代码。Tomcat的logs目录下catalina.out和localhost.log几乎能覆盖80%的坑。
5.1 连不上MySQL:error 2002 (HY000)和Access denied是两回事
现象一:Linux下执行mysql -uroot -p,报错信息是:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)原因:MySQL服务没启动,或者客户端默认走socket文件但文件不存在。常见于linux安装mysql后没有service mysqld start。
解决:先启动服务再连接。多数组件安装后,命令是systemctl start mysqld或systemctl start mysql,用systemctl status mysql确认状态是running。如果你非要用socket方式连接,需要知道socket文件实际路径,在my.cnf里查看socket配置,用mysql -uroot -p -S /var/lib/mysql/mysql.sock指定。
现象二:应用启动时日志报Access denied for user 'root'@'localhost' (using password: YES)。
原因:账号密码不对,或者密码插件不兼容。MySQL 8默认使用caching_sha2_password,而老项目连接的驱动版本不支持,即使密码正确也会拒绝连接。
解决:先用Navicat for MySQL或MySQL Workbench试试同一组账号密码能不能连上。如果图形工具能连而应用连不上,优先怀疑驱动版本,把mysql-connector-java升级到8.x。如果root密码你确实忘了,在MySQL 8里可以临时跳过授权表重启,但这一步操作复杂,更稳妥的方法是修改密码插件:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;这条SQL把root的认证插件改成mysql_native_password,老驱动的兼容性立刻变好。注意:MySQL 5.7不需要这条,别照抄。
5.2 页面中文乱码:Tomcat、JSP、MySQL三处编码必须一致
现象:首页新闻标题和正文全变成问号或者乱码,但英文正常。
原因:字符集不统一。浏览器拿到页面时按GBK解码,数据库里的数据却是UTF-8,或者数据库写入时已经是乱码,到页面这层神仙也救不回来。中文乱码不是“改一个地方就好”,而是三处编码必须对齐。
解决按顺序做:第一,确认数据库和表是utf8字符集,建库时执行CREATE DATABASE news DEFAULT CHARACTER SET utf8,已经建错的库可以用ALTER DATABASE改。第二,检查jdbc.url里有characterEncoding=utf8,这一点在第2.2节已经强调过。第三,确认Tomcat的连接器强制UTF-8:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>第四,JSP页面顶部确保有这一行:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>这里最容易忽略的是POST请求的中文乱码。SpringMVC在web.xml里如果没有配置CharacterEncodingFilter,POST提交的新闻标题会是乱码。这不算玄学,就是web.xml缺一个过滤器,配一下就稳定:
<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>排查顺序建议是:先用Navicat看数据库里存的值是不是中文。如果库里是乱码,问题在导入SQL脚本或写入环节;如果库里是正常中文而页面乱码,问题在Tomcat或JSP编码。
5.3 端口被占用和404:别把“Tomcat启动了”和“项目起来了”混为一谈
现象:启动Tomcat时控制台报错Port 8080 required by Tomcat v8.5 Server is already in use;或者Tomcat没有任何报错,但访问http://localhost:8080/news/一直404。
原因:端口占用最常见于之前启动过另一个Tomcat实例,或者IDEA异常退出后残留java进程。404则复杂一些,可能是war包没成功解压、访问路径不对、SpringMVC把请求转发到了不存在的JSP文件。
解决:端口占用先用命令找到占用进程,再决定是杀掉还是换端口。
# Linux/macOS lsof -i:8080 # Windows netstat -ano | findstr 8080查到PID后,前一个Tomcat进程kill -9 PID。如果换端口,要同时改server.xml里的Connector端口和访问URL,项目上下文路径不变。
404排查要看当前进展到哪一步。先访问http://localhost:8080/,能看到Tomcat默认首页,说明Tomcat本体正常,问题在项目部署;连默认首页都访问不到,Tomcat启动本身就有问题,去看logs/catalina.out。项目404时,检查webapps目录下有没有news目录生成。如果没有,说明war包没被解压,原因通常是war包损坏或复制到webapps后Tomcat没重启。如果解压了还404,下一步打开logs/localhost.log,看SpringMVC是否把/news/news/list映射到了某个Controller方法,以及视图解析器拼出来的JSP路径是否存在。视图解析器配置和实际目录不一致时,也会出现“系统好像通了但页面404”的诡异情况。
5.4 启动报ClassNotFoundException:Maven依赖没打进去
现象:Tomcat启动几秒后失败,localhost.log里报:
java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener原因:项目在IDE里编译没报错,但Servlet容器运行时找不到Spring的jar包。这通常是Maven依赖没有被发布到WEB-INF/lib目录下。SSM项目最怕这种“编译通过、启动失败”的场景,报错信息越简短,越容易让人怀疑是环境问题。
解决:如果是Eclipse,右键项目选择Maven > Update Project,强制刷新依赖。如果是IDEA,检查Project Structure里的Artifacts,看WEB-INF/lib下有没有Maven Dependencies。最省事的验证方式是执行一次mvn clean package,然后直接看target/news/WEB-INF/lib目录里的jar包数量。如果lib下没有spring-web和mybatis等核心jar,说明打包配置有问题,在pom.xml里检查packaging是不是war,以及有没有误把依赖scope设成provided。
另一个隐蔽的同类问题是jar包重复。项目里webapp/WEB-INF/lib下已经手动拷了一份spring jar,Maven又拉了一份,版本不一样时Tomcat按字母顺序加载,先加载到老版本就可能报方法找不到或NoSuchMethodError。这种坑的排查方法是:删掉lib下手动添加的jar,全部交给Maven管理,保持版本统一。老项目常用的止血方式是“从lib下找一份能跑的完整jar集合”,但你既然有pom.xml,就花10分钟把依赖理清楚,后面改动起来才不心虚。
6. 答辩前的最后验证:把请求链路和数据库关系讲清楚
项目跑通了,代码也改了两三个功能,接下来要做的事不是继续加功能,而是把自己放进答辩场景里验证一遍。我见过不少同学在演示环节一切正常,老师一问“你这个新闻列表是怎么查出来的”就卡壳。归根到底是只记住了操作步骤,没记住数据流。
找一个最简单的请求,比如新闻列表。从浏览器输入URL开始,到页面渲染完成,你能不能在纸上画出这条线:URL进入DispatcherServlet,DispatcherServlet根据@RequestMapping找到NewsController的list方法,Controller调用NewsService,NewsService注入NewsMapper,NewsMapper的XML执行SELECT语句查MySQL,结果集封装成List 返回,Controller把列表放进Model,视图解析器定位到list.jsp,JSP渲染出HTML。这条线能默写出来,SSM框架部分就不会失分。
数据库关系也要能口头讲清楚。建议在答辩前把三张核心表的字段和关系画一遍:news表通过category_id关联分类,通过author_id关联发布人。老师最容易追问的边界问题是“删除一个分类,这个分类下的新闻怎么办”。你可以答:表结构里建了外键约束,默认会拒绝删除有子记录的栏目,或者你在业务层先做判断,把该分类下新闻移走再删分类。这就是把表结构转化为业务逻辑的表达,比背概念更能说明你真的理解项目。
下面是老师常问问题的三个应答方向,提前对着镜子说两遍:
| 提问方向 | 应答要点 |
|---|---|
| SSM三个框架分别做什么 | Spring管对象,SpringMVC管请求分发,MyBatis管SQL映射 |
| 首页最新新闻是怎么实现的 | Mapper里SELECT按publish_time DESC排序,LIMIT控制条数 |
| 你在这个项目里改了哪些代码 | 说一个具体功能,比如分页或搜索,讲清楚Mapper、Service、Controller三层改动 |
最后一个验证技巧是打断点。在Controller的list方法第一行打一个断点,用Debug模式启动Tomcat,刷新页面。跟着调试器看一遍参数传入、Service跳转、Mapper执行,然后按F8走完。这一步走完,比看两遍视频教程都管用,你不仅知道“它跑起来了”,还知道“它是怎么跑起来的”。
个人的血泪经验是:当年我做毕业设计时,也是拿到一套SSM新闻系统,上来就急着改前端页面,结果后台登录一直失败,查了整整一天才发现数据库密码没改。后来老老实实把环境从头理了一遍,又对着断点走通了新闻列表的请求链路,答辩时老师问哪个类都不慌。那套系统最后拿到的分数不算高,但“能讲清楚”这件事确实帮了大忙。希望帮到你。
本文还有配套的精品资源,点击获取