资讯详情

基于SSM+JSP的学科竞赛管理系统设计实现与避坑指南

📅 2026/9/29 21:25:49 | 华诺云谱 👁 阅读
基于SSM+JSP的学科竞赛管理系统设计实现与避坑指南
做Java Web课设或毕设的同学大概率都绕不开一类经典题目基于SSMJSP的管理系统。这次要拆解的是一个非常典型的“Java基于SSMJSP的学科竞赛管理系统”这类题目在毕设选题库里出现频率极高网上虽然能找到不少相似项目但大多要么只有代码没有设计思路要么文档和实际工程对不上。我自己带过几届学生做类似系统也帮人排查过不少这类项目的报错今天就围绕这个题目把完整的拆解思路、技术选型逻辑、核心模块实现和容易踩的坑都整理出来希望对正在做课设、准备毕设答辩或者刚接触SSM整合开发的同学有帮助。这个系统的业务并不复杂核心就是围绕“学科竞赛”这件事把学生报名、教师审核、作品提交、评委评审、成绩发布这几条线串起来。难点在两点一是SSM三个框架整合时的配置细节二是JSP页面和后端交互时的数据流转。前者决定了项目能不能跑起来后者决定了系统好不好用。我会从这两条线分别展开先讲整体设计思路再落到表和代码层面最后把高频故障逐个拆开。1. 系统整体设计与技术选型1.1 为什么用SSMJSP而不是Spring Boot这是几乎每个选这个题目的同学都会问的问题。既然Spring Boot已经成了主流为什么还要用SSM加JSP这种组合答案很现实因为教学大纲和毕设要求往往还停留在SSM阶段很多学校的Java Web课程设计题目就是按SSM架构出的。另一个原因是SSM能让你真正理解Spring的IOC和AOP、MyBatis的SQL映射、SpringMVC的请求流转过程而Spring Boot把这些东西大量自动化了写起来快但面试被问到底层时容易答不上来。从技术上来说SSMJSP这套组合完全能支撑学科竞赛管理系统这种中小型项目。它的典型请求流程是浏览器发请求到SpringMVC的DispatcherServletHandlerMapping找到对应的Controller方法Controller调用Service层Service调用Mapper接口Mapper通过MyBatis与MySQL交互返回结果再经过Model和ViewResolver渲染到JSP页面。每一步都是显式配置的出了问题很容易定位。选这套技术栈还有一个重要原因JSP作为视图层在传统项目中依然有大量存量系统在用很多公司的老项目就是SSMJSP写的维护这些项目需要看得懂这套东西。你把这个系统做完等于同时掌握了Spring容器管理、SpringMVC请求映射、MyBatis动态SQL和JSP标签库这套技能组合在维护老项目和应对面试时都实用。1.2 功能模块与角色权限设计学科竞赛管理系统一般包含三类角色学生、教师评审、管理员。不要一上来就写代码先把用例图画清楚这个系统80%的工作量其实在权限控制和状态流转上。学生端的功能是注册登录、浏览竞赛公告、在线报名、上传作品材料、查看自己的报名状态和最终成绩。教师端的功能是查看分配给自己的评审任务、下载学生作品、打分并填写评语、提交评审结果。管理员端的功能是发布竞赛信息、管理参赛学生信息、分配评审任务、审核报名资格、发布获奖名单、统计竞赛数据。这里有一个关键设计点竞赛的状态流转。我建议设计四个状态——报名中、评审中、已结束、已发布。所有操作都围绕竞赛状态来控制报名中才能报名和审核评审中才能打分结束后才能生成成绩排名。很多新手项目不做状态控制导致学生能随时报名、评委能反复改分这类逻辑漏洞在答辩时最容易被老师挑出来。角色权限的实现不建议引入Spring Security或Shiro对于这种课设规模的项目用SpringMVC拦截器加Session判断就足够了。具体做法是自定义一个HandlerInterceptor在preHandle方法里判断当前Session中保存的用户角色再判断请求的URL前缀是否匹配该角色的权限范围。比如/admin/**必须管理员角色才能访问/student/**必须是学生角色。这样做出来的权限控制够用且容易讲解答辩时能说清楚拦截器的工作原理比背一个框架配置更有说服力。1.3 项目骨架与目录结构规范SSM项目的标准目录结构是这样的先照这个搭后面容错率高src/main/java ├── com.xxx.competition │ ├── controller │ │ ├── admin │ │ ├── teacher │ │ └── student │ ├── service │ │ └── impl │ ├── mapper │ ├── entity (或 pojo) │ ├── interceptor │ ├── common (通用返回结果、常量类) │ └── utils src/main/resources ├── jdbc.properties ├── mybatis-config.xml ├── spring-mvc.xml ├── spring-mybatis.xml ├── generatorConfig.xml (用MyBatis Generator生成代码) src/main/webapp ├── WEB-INF │ ├── web.xml │ └── jsp │ ├── admin │ ├── teacher │ ├── student │ └── common ├── static │ ├── css │ ├── js │ └── imagesController包按角色分包是因为学科竞赛系统天然存在三种角色的操作边界按角色分包能够让权限控制更清晰也方便以后扩展。实体类建议用MyBatis Generator生成这个工具能从数据库表直接生成实体类、Mapper接口和XML映射文件能省掉大量的重复造轮子时间。生成之后只需要在Mapper接口里补充自定义的联表查询方法即可。资源文件这块要特别说一下SSM项目最常见的启动报错80%发生在Spring和MyBatis的配置文件上。配置文件的命名和路径最好固定下来spring-mybatis.xml负责管理数据源、SqlSessionFactory和Mapper扫描spring-mvc.xml负责Controller扫描、视图解析器和静态资源放行。spring的配置文件通过web.xml里的ContextLoaderListener加载SpringMVC的配置通过DispatcherServlet加载这两者不能混。2. 数据库设计与核心表结构2.1 表结构设计不要一张大表走天下学科竞赛管理系统最少需要六张表用户表、角色表可选、竞赛表、报名表、作品表、评分表。很多新手图省事把用户角色字段直接写成字符串塞在用户表里把作品和评分塞在报名表里这样前期写起来快但后期统计和扩展会非常痛苦。我给出的核心表设计如下用户表t_user字段名类型说明idint 主键自增用户IDusernamevarchar(50) 唯一登录账号passwordvarchar(100)密码MD5加密存储real_namevarchar(50)姓名roletinyint1管理员 2教师 3学生student_novarchar(20)学号仅学生majorvarchar(50)专业phonevarchar(11)联系方式create_timedatetime注册时间密码字段这里要特别提出批评我看到大量课设项目把密码明文存数据库这是非常糟糕的习惯。至少要做一个MD5加盐处理或者直接用jbcrypt的BCrypt加密。答辩时老师如果问“密码安全怎么处理”你能答出加盐哈希印象分会明显不一样。竞赛表t_competition字段名类型说明idint 主键竞赛IDnamevarchar(100)竞赛名称descriptiontext竞赛简介typevarchar(50)竞赛类别数学建模/程序设计/英语等organizervarchar(100)主办方signup_startdatetime报名开始时间signup_enddatetime报名截止时间review_startdatetime评审开始时间review_enddatetime评审截止时间statustinyint0未开始 1报名中 2评审中 3已结束award_settingvarchar(200)获奖设置如一等奖2个二等奖4个竞赛表里的状态字段是整个系统的时间轴控制核心。管理员创建竞赛时根据当前时间自动算出status或者每次查询时用SQL里的NOW()与时间字段比较得出当前状态。用NOW()比较的方式更稳妥能避免因人工修改数据导致状态不一致。报名表t_signup字段名类型说明idint 主键报名IDcompetition_idint 外键竞赛IDstudent_idint 外键学生IDteam_namevarchar(100)队伍名称可个人参赛teammatesvarchar(200)队友学号逗号分隔teacher_idint指导老师ID可空statustinyint0待审核 1已通过 2已拒绝signup_timedatetime报名时间final_rankvarchar(20)最终获奖等级可空报名表设计成什么粒度要看系统定位如果定位是个人参赛那报名表直接关联学生ID即可如果允许组队就要引入队伍表或者用teammates字段存储队友信息。我这里用了teammates字段存队友学号的方式好处是简单坏处是没法做队友维度的统计比如查“某个学生参加了哪些队”。如果毕设要求不高这种方式够用但如果你有余力建议拆一个t_team表和t_team_member表数据更规范。作品表t_work字段名类型说明idint 主键作品IDsignup_idint 外键关联报名表work_titlevarchar(100)作品名称work_desctext作品说明file_urlvarchar(200)附件存储路径submit_timedatetime提交时间versiontinyint版本号作品表加入version字段是我比较推荐的一个设计。学生上传作品后如果发现传错了允许重新上传每次上传version加1。这样既实现了“允许修改作品”又保存了历史版本评审老师可以对比查看。没有版本控制的话学生重新上传后老文件被覆盖一旦新文件有问题旧文件也找不回来了。评分表t_score字段名类型说明idint 主键评分IDwork_idint 外键作品IDteacher_idint 外键评审教师IDscoredecimal(4,1)得分如85.5commentvarchar(500)评语score_timedatetime评分时间评分表设计成“一名教师对一件作品只有一条评分”所以work_id和teacher_id要做联合唯一索引。计算总成绩时可以取多个评委的平均分规则可以在代码里写明。平均分的计算建议在SQL里完成AVG(score)一条语句搞定不要把所有分数查出来之后再用Java循环算那样既笨拙又有精度问题。2.2 多表关联查询怎么组织SQL系统里查询频率最高的几个场景学生查看自己的报名列表联查竞赛名和竞赛状态、教师查看自己的评审任务联查作品信息、竞赛信息、学生信息、管理员查看竞赛的报名统计按竞赛分组统计报名人数。这类多表查询建议直接写在Mapper的XML文件里用SQL的JOIN完成不要试图在Java代码里做内存关联。举一个典型的例子学生端“我的报名记录”页面需要展示报名ID、竞赛名称、竞赛状态、报名时间、我的报名状态。SELECT s.id AS signup_id, c.name AS competition_name, c.status AS competition_status, s.status AS signup_status, s.signup_time, s.final_rank FROM t_signup s LEFT JOIN t_competition c ON s.competition_id c.id WHERE s.student_id #{studentId} ORDER BY s.signup_time DESC注意这里用的是LEFT JOIN而不是INNER JOIN虽然报名记录一定能关联到竞赛表但LEFT JOIN能保证即使竞赛记录被异常删除报名记录依然能查出来页面不至于报错。查询结果不要用实体类接收直接在Mapper里定义一个ViewObject类比如SignupVO来接收联表查询结果这样能避免在实体类里塞一堆不属于它的字段。MyBatis的XML映射文件里resultType可以直接填VO类的全限定名只要SQL查询的列名和VO属性名能对应上。如果列名和属性名不一致用SQL别名来对齐比如c.name AS competitionName。这一招能省掉写一长串resultMap的麻烦。3. 环境搭建与配置要点3.1 JDK、Maven、Tomcat版本怎么搭配SSM是老技术栈版本搭配是第一个坑。很多同学按最新版的教程装环境结果运行时报各种莫名其妙的错。我这里给出经过大量验证的稳定搭配JDK 8这是SSM项目最稳妥的版本没有之一。JDK 11及以上运行老版本Spring比如Spring 4.x会有模块访问报错学习阶段别给自己找麻烦。Maven 3.6.x不要用太新的Maven3.9的某些版本和旧插件有兼容问题。Tomcat 8.5或9.0支持Servlet 3.1/4.0JSP支持完善。Spring 5.0.x或5.1.x、MyBatis 3.4.x或3.5.x、MyBatis-Spring 1.3.x。MySQL 5.7或8.0JDBC驱动用mysql-connector-java 5.1.49连MySQL8需要加时区参数。这里有一个细节很多人不知道spring-jdbc的版本和mysql-connector-java的版本要匹配。用MySQL 8.0时连接串里必须加serverTimezoneAsia/Shanghai和useSSLfalse否则报CLIENT_PLUGIN_AUTHENTICATION错误或时区错误。如果你用的是8.0.33版本驱动URL要写成jdbc:mysql://localhost:3306/competition?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrueallowPublicKeyRetrievaltrue这个参数也容易被忽略MySQL 8.0使用caching_sha2_password认证时不加这个参数连不上。Maven的pom.xml里核心依赖列出来就是spring-webmvc、spring-jdbc、mybatis、mybatis-spring、mysql-connector-java、jackson-databind处理JSON、jstl和standardJSP标签库、javax.servlet-api和javax.servlet.jsp-apiprovided作用域、commons-fileupload文件上传、log4j和slf4j日志。注意servlet-api和jsp-api的scope一定要是provided否则会和Tomcat自带的冲突启动秒报错。3.2 web.xml与Spring配置文件的关键配置web.xml是SSM项目的入口配置文件新手经常在这里栽跟头。完整的web.xml要包含四样东西ContextLoaderListener加载Spring父容器、DispatcherServlet加载SpringMVC子容器、字符编码过滤器、欢迎页面配置。字符编码过滤器必须放在所有过滤器最前面并且要配置forceEncodingtrue否则POST请求的中文永远乱码。filter filter-nameencoding/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencoding/filter-name url-pattern/*/url-pattern /filter-mappingSpringMVC配置里的重点有两个视图解析器和静态资源放行。视图解析器用InternalResourceViewResolver前缀设成/WEB-INF/jsp/后缀.jsp这样Controller里return student/signupList就对应/WEB-INF/jsp/student/signupList.jsp。把JSP放在WEB-INF下是一个好习惯用户不能通过URL直接访问JSP文件必须经过Controller安全性好了很多。静态资源放行必须在spring-mvc.xml里配不然CSS、JS、图片全被DispatcherServlet拦截页面样式全丢mvc:resources mapping/static/** location/static/ /还要记得开启SpringMVC注解驱动mvc:annotation-driven/如果不加这个RequestMapping、ResponseBody这些注解全部不生效这是SSM项目里最高频的“配置了但没反应”的原因。3.3 分页插件与通用工具的选择分页是这类管理系统躲不开的需求。我的建议是直接用PageHelper一个MyBatis的分页插件引入依赖后在配置里加一行插件注册查询前调用PageHelper.startPage(pageNum, pageSize)查询结果用PageInfo接收就能拿到总条数、总页数等所有分页数据。它基于拦截器实现在SQL执行前自动拼接LIMIT对业务代码零侵入。但PageHelper有一个使用禁忌startPage只对紧接着的下一条查询生效。如果查询前有其他查询操作分页就会串数据。正确写法是PageHelper.startPage(pageNum, pageSize); ListSignupVO list signupMapper.selectMySignups(studentId); PageInfoSignupVO pageInfo new PageInfo(list);在Controller返回页面时把pageInfo放进ModelJSP里用pageInfo.list遍历数据用pageInfo.total显示总数用pageInfo.pages渲染分页按钮。这套组合写起来非常顺手推荐直接复用。其它工具类建议自己写几个通用的MD5Util用于密码加密DateUtil用于日期格式化Result用于统一的Ajax返回封装包含code、msg、data三个字段UploadUtil用于文件上传路径处理。这些工具类不要到处复制粘贴统一收拢到common或utils包里后续维护只改一处就够了。4. 核心模块实现与难点拆解4.1 登录与验证码功能的实现登录模块是所有管理系统的门面也是答辩时必问的模块。SSM项目里登录的基本逻辑是用户提交用户名和密码Controller调用Service层Service先根据用户名查出用户再对比密码哈希值匹配则把用户对象写入Session跳转到对应的首页不匹配则返回错误消息。密码对比时要加一个细节先根据用户名查用户如果用户不存在直接返回“用户名或密码错误”不要告诉用户到底是用户名错了还是密码错了防止恶意探测账号。者也算一个小的安全意识体现。验证码可以用传统的Servlet画图方式实现也可以用Kaptcha这个开源工具。Kaptcha的使用方式是配置一个ProducerBeanController里调用createImage()生成BufferedImage把验证码文字存Session图片输出到响应流。前端提交时把输入的验证码和Session里的值对比。这里有一个被忽略的细节验证码对比完之后无论对错都要销毁Session里的旧验证码防止重复使用和暴力破解。登录成功后按角色跳转我建议在Controller里写一个switch判断而不是让前端页面根据角色自行跳转这样角色逻辑集中在后端前端只负责展示更安全也更清晰。4.2 拦截器实现权限控制完整配置与代码权限拦截器是SSM系统里最值得单独讲清楚的部分。传统的做法是实现HandlerInterceptor接口重写preHandle方法。一个合格的权限拦截器要处理三种情况未登录、已登录但角色不匹配、Ajax请求的未登录状态。未登录的常规处理重定向到登录页。但这会遇到一个体验问题用户未登录时点击页面上的“报名”按钮如果是普通链接重定向到登录页没有任何问题但如果这个请求是Ajax发起的重定向返回的是一整个HTML页面前端JavaScript拿到这个HTML后无法解析功能就失效了。所以拦截器里要判断请求是否带X-Requested-With: XMLHttpRequest头如果是Ajax请求直接返回401状态码让前端统一处理跳转到登录页。拦截器的注册在spring-mvc.xml里配置mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/**/ mvc:exclude-mapping path/static/**/ mvc:exclude-mapping path/captcha/**/ bean classcom.xxx.competition.interceptor.LoginInterceptor/ /mvc:interceptor mvc:interceptor mvc:mapping path/admin/**/ bean classcom.xxx.competition.interceptor.AdminInterceptor/ /mvc:interceptor /mvc:interceptors登录拦截器负责全局登录校验管理员拦截器在学生或教师角色访问/admin/**时直接拦截并给提示。两个拦截器的顺序需要注意登录拦截器必须在前角色拦截器在后否则未登录用户直接访问/admin/**时角色拦截器会先拿到一个空的Session用户无法做角色判断。JSP页面上的权限展示用c:if test${sessionScope.user.role 1}这样的JSTL判断来控制在页面上显示哪些菜单项。注意判断对象要从sessionScope里取不是从requestScope里取很多同学写成了${user.role}然后在controller里没往model里塞User对象导致页面上的权限菜单永远不显示。4.3 竞赛发布与报名状态流转管理员发布竞赛时页面表单要提交的信息包括竞赛名称、简介、类型、主办方、报名起止时间、评审起止时间、获奖设置。这里比较关键的是状态字段的处理方式我建议后端不接收前端提交的status字段而是根据当前时间和报名起止时间、评审起止时间自动计算。Controller里收到请求后用当前时间与时间字段比较手动算出status再存入数据库。这个做法的好处是防止前端伪造状态值也避免管理员发布竞赛时忘记手动选择状态导致状态不对。计算逻辑可以封装在Service层的一个私有方法里private int calcStatus(Date now, Date signupStart, Date signupEnd, Date reviewStart, Date reviewEnd) { if (now.before(signupStart)) return 0; // 未开始 if (now.before(signupEnd)) return 1; // 报名中 if (now.before(reviewEnd)) return 2; // 评审中 return 3; // 已结束 }学生报名时Controller层要做的第一件事不是直接入库而是校验状态。如果竞赛状态不是“报名中”直接返回“当前不在报名时间范围内”。这个校验既要在Service层做也要在JSP页面上通过判断竞赛状态来控制报名按钮是否可点击。双重校验是为了防止用户通过手工构造请求绕过页面限制。报名列表展示时学生端看到的状态要非常直观待审核显示橙色标签、已通过绿色、已拒绝红色。这些标签样式用JSTL加CSS类实现把状态值映射成对应的颜色类名。JSP里用${signup.status}判断对应的CSS类写死在页面的style里或者单独的CSS文件里。4.4 作品上传与多版本管理作品文件上传是SSM项目里另一个高频技术点用的组件是commons-fileupload。在spring-mvc.xml里配置一个CommonsMultipartResolver设置最大上传大小和编码方式bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver property namemaxUploadSize value104857600/ property namedefaultEncoding valueUTF-8/ /bean文件保存路径要注意一个核心原则不能把文件直接存到Web应用目录下。因为项目重新部署时上传的文件会被清空而且如果Web应用目录有写入权限限制上传会直接失败。正确的做法是存到服务器的一个绝对路径比如/home/upload/competition/或者Windows下的某个专门目录。数据库里只存文件的相对路径如/upload/2025/04/xxx.pdf然后通过配置映射让Tomcat能访问到这个目录。SpringMVC配置里可以加一个虚拟目录映射让Tomcat将/upload/**的请求映射到服务器本地目录mvc:resources mapping/upload/** locationfile:/home/upload/competition/ /这样浏览器就能通过/upload/2025/04/xxx.pdf直接访问到上传的文件。如果用的是Tomcat 9也可以在server.xml里配置Context的docBase属性但SpringMVC的配置方式更简洁而且不用动服务器配置文件。保存文件时用UUID重命名是标配操作避免中文文件名和重复名字带来的乱码和覆盖问题。原始文件名要保留可以在数据库里增加一个字段存储原始文件名下载时用它作为Content-Disposition头。多版本控制的实现逻辑提交作品时先查当前signup_id是否已有作品记录。有的话version加1把旧记录标记为无效或在同一记录里更新文件路径保留旧文件不删除没有的话version设为1插入新记录。评审时只查最高版本的记录。文件名的UUID和version字段拼接比如work_12_v3.pdf这样同一次报名的多个版本文件可以在服务器上同时存在不会被覆盖。4.5 成绩计算与统计展示成绩模块的核心是平均分和排名。评委打分完成后管理员在后台点“计算成绩”按钮系统根据所有有效评分计算每个作品的平均分再按竞赛分组排序生成获奖名单。SQL层面的实现思路是SELECT w.id AS work_id, AVG(sc.score) AS avg_score, COUNT(sc.id) AS review_count FROM t_work w LEFT JOIN t_score sc ON w.id sc.work_id WHERE w.signup_id IN ( SELECT id FROM t_signup WHERE competition_id #{competitionId} ) GROUP BY w.id ORDER BY avg_score DESC拿到这个结果后再结合竞赛的获奖设置按名次划定获奖等级。比如一等奖设置2个那排名前2的是一等奖二等奖4个再取接下来4个以此类推。这里要注意并列排名的问题如果第3名和第4名平均分相同是按照“成绩优先、提交时间优先”还是直接跳过要在代码里做明确约定。我通常建议直接保留并列两个人都算二等奖但后面的三等奖名额对应减少。统计展示端管理员的首页可以放几个简单的统计卡片当前正在进行的竞赛数量、报名总人数、待评审作品数、已发布成绩数。这些统计用几条聚合SQL就能查出来不需要引入额外的图表库。如果论文里需要展示“各学院参赛人数分布”之类的图表可以引入ECharts后端提供JSON接口前端用Ajax拉数据再渲染。注意用ResponseBody返回JSON时日期类型默认会被序列化成long类型时间戳如果前端需要展示日期要在日期字段上加JsonFormat(pattern yyyy-MM-dd)注解。4.6 传统JSP项目如何打war包部署到服务器这类课设系统提交时通常要打war包部署。在IDEA里给项目添加Web facet后Build菜单里就有Build Artifacts选项选择xxx:war exploded是开发调试用xxx:war是最终部署包。打包之前一定要改好两个地方jdbc.properties里的数据库连接地址改成服务器地址日志路径改成服务器路径。有些同学发现war包打出来部署到Tomcat后JSP写的修改没有生效。这是因为IDEA打包时没有重新编译JSP或者Tomcat开启了缓存。实际上Tomcat对JSP默认会做增量编译修改JSP后需要清空Tomcat的work/Catalina/localhost目录让Tomcat重新编译。这个坑非常隐蔽很多老手也会被折腾一阵。如果服务器环境限制不能装JDK 8该怎么办有些人的服务器装了JDK 17跑Spring 4的SSM项目直接报java.lang.IllegalStateException: Unable to process parts之类的错。最省事的办法是下载一个绿色版JDK 8在Tomcat的catalina.bat或setenv.sh里显式指定JAVA_HOME指向JDK 8目录。这比改代码兼容新JDK现实得多。5. 常见问题排查与避坑指南5.1 高频报错速查表现象原因解决方案启动Tomcat报ClassNotFound依赖冲突或打包没带全检查pom.xml依赖确认provided作用域只用于servlet-api和jsp-api执行mvn clean package重新打包访问页面报404控制台无错误请求路径没映射到Controller或JSP路径与视图解析器前缀不匹配检查Controller的RequestMapping值检查视图解析器的prefix和suffix拼接后的文件是否存在页面CSS、JS全部失效静态资源被拦截器拦截或mvc:resources没配置确认spring-mvc.xml里有mvc:resources mapping/static/** location/static//前端返回JSON中文乱码SpringMVC默认用ISO-8859-1编码输出JSON在mvc:annotation-driven里配置消息转换器将字符串编码改为UTF-8上传文件报FileSizeLimitExceededException配置文件上传大小限制过低调大CommonsMultipartResolver的maxUploadSize同时检查Tomcat的maxPostSize配置MyBatis报Invalid bound statementMapper接口和XML没有绑定检查XML文件目录是否在resources/mapper下检查Mapper接口全限定名和XML的namespace是否一致访问项目路径时只显示Tomcat欢迎页应用未部署成功检查war包解压目录结构确认WEB-INF/web.xml存在且配置正确报Failed to configure a DataSource数据源参数错误检查jdbc.properties里的URL、用户名、密码确认MySQL服务启动了5.2 中文乱码问题彻底根治中文乱码在SSM项目里是老生常谈但乱码的来源真的非常多。我总结的排查顺序是前端页面JSP的pageEncoding设置为UTF-8JSP头部加% page contentTypetext/html;charsetUTF-8 languagejava %Tomcat的server.xml里Connector加URIEncodingUTF-8处理GET请求的中文参数SpringMVC的CharacterEncodingFilter配置forceEncodingtrue数据库连接URL里带characterEncodingutf8数据库表本身和列本身是utf8mb4编码。五层全对中文基本不会乱。还有一个容易被忽略的环节是文件下载时的文件名乱码下载的文件名如果是中文需要对文件名做URL编码再拼到Content-Disposition里否则浏览器直接乱码。写法参考response.setHeader(Content-Disposition, attachment;filename URLEncoder.encode(realName, UTF-8));如果你自己写完发现页面还是乱码先去检查JSP文件的编码是不是真的UTF-8。IDEA的全局编码和项目编码两处都要设置成UTF-8某些情况下文件本身是GBK编码即使页面声明UTF-8也白搭乱码会一直存在。5.3 答辩时容易被问的问题清单这部分是给准备答辩的同学准备的。老师围绕SSMJSP这个组合问得最多的问题如下提前准备好就不会在现场卡壳Spring的IOC是什么你的项目里哪些地方体现了IOC答对象创建和依赖注入交给Spring容器管理比如Controller注入Service用的是AutowiredService注入Mapper也一样。SpringMVC的请求处理流程是什么答DispatcherServlet - HandlerMapping - Controller - Service - Mapper - 视图解析 - JSP渲染。MyBatis中#{}和${}的区别答#{}是预编译占位符防止SQL注入${}是字符串拼接有注入风险但可以用于动态排序字段。JSP的九个内置对象有哪些简单列举即可答出request、response、session、out、application基本就够了。你的系统如何防止SQL注入答全部使用MyBatis的#{}参数绑定不让用户输入直接拼接SQL。不同角色的权限是怎么控制的答SpringMVC拦截器加Session角色判断以及页面上的JSTL条件渲染。用户密码是怎么存储的答MD5加盐或BCrypt哈希不用明文。为什么竞赛状态要自动计算而不是手填答避免数据不一致保证业务状态准确。每个问题都要结合自己项目的具体代码来讲不要背概念。比如问到IOC你就说“我的SignupService加上了Service注解SignupController里用Autowired注入它Spring容器在启动时自动创建Service实例并注入到Controller里”这样老师就知道你是真的动手做了。5.4 上线部署与安全注意事项如果这个系统是要真正上线测试而不只是本地运行有几个安全细节必须处理。第一管理员初始密码登录后要强制修改第二登录失败次数达到5次时锁定账号短时间内不能继续尝试第三上传文件要做类型校验只允许doc、docx、pdf、zip、rar这些指定扩展名防止用户上传jsp或jspx后门文件第四MySQL不要用root账号连接应用创建专用账号并只授予该业务库的权限。文件上传的安全校验尤其重要。攻击者可以通过构造合法的multipart请求绕过前端的文件类型限制所以后端必须二次校验扩展名和Content-Type。如果文件扩展名是jsp、jspx、war直接拒绝。更稳妥的做法是将上传目录放在Web应用目录之外且配置访问映射时关闭脚本执行权限。数据库方面给不同角色的账号做最小权限分配也是加分的点。学生的账号只读自己的报名和成绩数据教师的账号只能读取分配给自己的评审任务这些都可以通过SQL里带WHERE teacher_id #{currentTeacherId}来实现并不是非要做很复杂的数据权限框架。写在这个题目之后的一点心得学科竞赛管理系统这个题目我是看着一届又一届的同学拿它做课设的。很多人开始觉得这个题太普通觉得没有技术含量但真正做完会发现一个看似简单的管理系统其实就是大多数企业内部系统的缩影——有用户角色有业务流程有状态流转有数据统计。把这一套逻辑吃透往后无论是学Spring Boot还是微服务底子都在这里。我个人在实操中最大的体会是这种系统真正的复杂度不在CRUD而在状态控制和异常处理。你把竞赛状态流转想清楚了把“用户乱传参数、反复提交、上传错误文件”这些异常场景都考虑到了整个系统的质量就上来了。代码量反而只是水到渠成的事。最后再分享一个小经验做完这个项目之后可以把Mapper接口从单表CRUD改装成复杂统计SQL把状态设计和权限拦截器单独抽出来整理成笔记。面试时这些具体业务场景比一个光秃秃的增删改查项目有说服力得多。祝你的项目答辩顺利。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑