每年到了毕设选题季,总有一批学生盯上“教务管理系统”这类题目:业务好理解、资料多、答辩论得清。但真上手写代码之后,很多人又会被“springboot + web service技术”这几个字绊住——网上搜出来的教程一半是SSM老项目,另一半则是纯REST风格,跟题目里“web service技术”完全对不上。这篇文章我按一个可落地的毕业设计路线来聊:Spring Boot做主体框架,Web Service技术作为对外接口层落地,把教务管理里用户、课程、选课、成绩这几条主链路串起来,同时把权限、并发、部署、答辩这些容易被追问的点一并讲透。内容尽量贴近实际开发顺序,适合准备做此类课题、或者已经在写代码但卡在某个环节的同学参考。
1. 教务管理系统这个选题:为什么值得做,但没那么简单
1.1 它在毕设选题中的定位
教务管理系统属于典型的信息管理系统,放在计算机毕业设计里看,属于“安全牌”和“平庸牌”的分岔口。说它安全,是因为业务需求明确,学生、教师、课程、成绩这些对象大家在学校里天天接触,不需要额外科普,需求分析、用例图、E-R图都能顺畅画出来。说它容易平庸,是因为这类系统做到最后往往就是一堆增删改查页面,论文没什么可写的,答辩时老师问两句就露怯。
所以做这个题目的第一个原则是:不要只做CRUD,要在业务规则上做出差异化。比如选课时防止超选、退课后的名额回补、成绩只能由开课教师录入、一门课程不能被同一个学生重复选择,这些约束在初版功能里就应该体现。它们不复杂,但能证明你理解“系统”而不是“代码集合”。
第二个原则是,把“web service技术”这个点真正落进代码里。很多同学用Spring MVC写完后发现,自己全程用的是RESTful接口,和题目中的“web service”确实有关系,但答辩时容易说不清。稳妥做法是主体功能用Spring Boot + Spring MVC模板渲染,再额外用JAX-WS发布一组SOAP服务,比如成绩查询、教师课程列表查询,作为对外服务接口层。
1.2 业务边界:哪些功能必须做,哪些直接砍
教务管理系统的全貌很大,排课、教室调度、教材管理、学费核算、评教这些都属于教务范畴,但毕设周期根本做不完。建议把功能收敛为一条主线:基础档案 + 培养环节。具体必要功能我列一个MVP清单,照着这个顺序开发不会跑偏:
| 模块 | 核心功能 | 涉及角色 |
|---|---|---|
| 登录认证 | 账号密码登录、退出、密码修改 | 所有角色 |
| 用户权限 | 角色区分、页面访问拦截 | 管理员 |
| 学生管理 | 学生信息维护、按学号/姓名查询、按班级筛选 | 管理员 |
| 教师管理 | 教师档案维护、所属院系统计 | 管理员 |
| 班级/专业管理 | 班级、专业、院系基础数据维护 | 管理员 |
| 课程管理 | 课程基本信息维护,包括学分、学时、任课教师 | 管理员 |
| 开课管理 | 每学期生成课程记录,设定选课容量 | 管理员 |
| 选课管理 | 学生选课、退课、选课名单查看 | 学生、教师 |
| 成绩管理 | 成绩录入、修改、成绩查询、加权统计 | 教师、学生 |
| 数据统计 | 学生选课门数统计、课程选课人数统计 | 管理员 |
排课算法、智能推荐、复杂权限框架这些内容,初版不要碰。如果后面中期检查觉得进度快,再往外扩一两个功能作为加分项,比如成绩单导出、按学分绩点排名。把主链路做扎实,比堆砌一堆半成品功能要有效得多。
我在给同学改代码时见过一个很典型的反面案例:表建了十几张,控制器写了一百多个方法,但选课记录没有任何唯一性约束,同一个学生能把同一门选修课选三次。这种系统演示时看起来很热闹,答辩时一查数据就得露馅。基础约束和数据一致性,才是这种项目的血和肉。
2. 技术栈取舍:springboot做主框架,web service怎么落地
2.1 为什么选Spring Boot而不是纯Spring MVC
现在再让学生从零搭一个SSM架构的XML配置工程,已经不太符合实际工作情况。Spring Boot的价值在于自动配置和约定优于配置:内嵌Tomcat、默认依赖管理、Starter机制直接把常用组件的配置包好。对毕业设计来说,选择Spring Boot至少有三个实打实的好处:
第一,工程结构简洁,不需要写一堆XML配置文件,精简掉大量和业务无关的配置代码,论文里也能用更短的篇幅讲清楚技术方案。第二,本地开发调试直接运行主类启动服务,不用安装独立的Tomcat或者Jetty,部署到服务器时也可选择直接打jar包运行。第三,Spring Boot对Spring生态的兼容性最好,集成MyBatis、Spring Security、Apache CXF这些组件都有成熟的Starter方案。
选版本的时候注意一点:如果你用的是JDK8,直接选Spring Boot 2.7.x系列最省心;如果学校环境或者你个人装了JDK17以上,再考虑Spring Boot 3.x。下面的代码示例统一按JDK8 + Spring Boot 2.7.x来写,这是当前毕业设计环境里兼容性最高的组合。
2.2 Web Service两种实现:SOAP还是REST,毕设怎么选
“Web Service”这个术语其实有广义和狭义两套理解。广义上凡是基于HTTP提供跨系统服务能力的都可以叫Web Service,RESTful接口也算;狭义上主要指基于SOAP协议、用XML描述消息、并通过WSDL文档对外公布接口定义的分布式服务方式。如果你的题目出现“web service技术”但没写“rESTful”,很多答辩老师的第一反应是“你是不是要讲SOAP”,这时候只做JSON接口是有点风险的。
我的建议是:两者都做,但分工明确。系统内部以及浏览器页面请求,走常规的Spring MVC接口;对外提供教务数据服务时,用JAX-WS发布SOAP Web Service,例如成绩查询服务、教师课表查询服务,服务通过WSDL对外暴露。这样系统本身很好用,也有标准Web Service的落地证据。
实现SOAP服务时主要用Apache CXF框架,它是Java生态里比较主流的Web Service框架,和Spring Boot有对应的Starter集成。它在服务端负责把Java接口按照JAX-WS规范发布成SOAP端点,自动生成WSDL;在客户端则可以用wsimport或CXF自带的客户端工厂生成调用代码。后面第4章我会把从引入依赖到测试调用的完整过程写出来。
2.3 开发环境与版本组合
为了避免大家照着教程做时出现版本错位,我把一份经过验证的开发环境组合放在表格里,供直接参考:
| 组件 | 版本建议 | 说明 |
|---|---|---|
| JDK | 8 | 兼容性最好,学校机房一般也有该环境 |
| Maven | 3.6.3及以上 | 依赖管理工具 |
| Spring Boot | 2.7.18 | 2.7系列最后的稳定版 |
| Apache CXF | 3.5.x/3.6.x | 与Spring Boot 2.7适配良好 |
| MyBatis | mybatis-spring-boot-starter 2.3.x | 如果用Spring Boot3则换成3.x版本 |
| MySQL | 5.7或8.0 | 字符集用utf8mb4 |
| IDE | IntelliJ IDEA | 社区版即可 |
如果你决定用JDK17 + Spring Boot 3.x,CXF记得换4.x版本,MyBatis Starter也要换3.x版本,否则运行时会报一类“ClassNotFoundException”或者依赖冲突。这个坑我帮人排查过很多次,每次都差不多,先看依赖版本是否匹配。
3. 从数据库表到功能模块:先画好一张依赖图再写代码
3.1 核心表结构设计
毕设级教务系统不需要做太复杂的数据建模,但核心表和关联关系要想清楚。通常至少要有这些表:用户表、角色表、学生信息表、教师信息表、班级表、课程表、开课记录表、选课记录表。我先说设计意图,再给关键SQL。
用户表存登录账号和密码,但不要把学生姓名、学号、性别这些塞在用户表里,而是用user_id去关联学生表或教师表。这样设计的理由是:一个账号可以只存在于用户体系,不一定对应学生或教师角色,后续要扩展管理员等角色也方便。角色字段我习惯直接放在用户表里,用一个字符串或整数表示,毕设规模完全够用,不用再单独做user-role关联表。
开课记录表是课程表的下游表。课程表只保存课程本身静态信息,比如课程代码、名称、学分;开课记录表保存“这学期开了哪门课、由哪位老师上、容量是多少、已选多少人”。这样同一个课程不同学期可以有不同的开课记录,也方便统计每个学期的选课情况。
选课记录表则连接学生和开课记录,同时直接承载成绩字段。一门学生选过的课对应一条记录,成绩就记在选课记录上,要不要单独建成绩表?我建议不要,加了反而要在逻辑上保证“每条成绩都必须对应一条选课记录”,白白增加复杂度。以下是选课记录表的简化版本:
CREATE TABLE `student_course` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `student_id` BIGINT NOT NULL COMMENT '学生ID,关联student.id', `course_open_id` BIGINT NOT NULL COMMENT '开课ID,关联course_open.id', `semester` VARCHAR(20) NOT NULL COMMENT '选课学期,如2024-2025-1', `score` DECIMAL(5,2) DEFAULT NULL COMMENT '成绩,未录入时为空', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0正常,1退课', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_student_course_open` (`student_id`, `course_open_id`, `semester`), KEY `idx_course_open_id` (`course_open_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='选课记录表';这里最重要的就是uk_student_course_open这个联合唯一索引。哪怕应用层漏做了重复判断,数据库也能兜底,保证同一个学生同一个学期同一门开课记录只能出现一次。
3.2 模块落地的推荐顺序
开发顺序建议按依赖关系来排,而不是按页面优先或者代码生成器优先。我推荐的顺序是:
- 先搭数据库,把表结构和基础数据准备出来,这是后面所有模块的地基。
- 先做登录认证和用户上下文。登录不做好,后面每个页面都没法验证“当前是谁”,权限拦截也无从谈起。
- 再做基础档案:班级、学生、教师。它们是选课和成绩的“主语”。
- 再推进课程和开课模块,把课程批量导入或单条维护做通。
- 接下来是选课退课,这是系统里最关键的业务模块,会涉及并发更新和唯一性约束。
- 最后做成绩模块和统计页面,因为成绩依赖选课数据,统计依赖前面所有数据。
这个顺序的好处是每一阶段都能独立测试。我不太建议一开始就配置好代码生成器把全部表的增删改查都生成出来,那样看起来很快,但到了业务逻辑集成阶段,大量生成代码反而让人不知道该改哪里。
3.3 容易翻车的业务约束
这块是教务系统区别于玩具Demo的关键,我把高频出的几个问题列一下:
- 重复选课:前面那张选课表加了联合唯一索引,代码里选课之前先执行一次查询校验,双保险。
- 超选名额:不要先查容量再判断再更新,要用原子更新来兜底。以下是一个比较稳妥的写法:
int updated = courseOpenMapper.reduceOrOccupy( courseOpenId, 1, // 本次要占用的名额 maxCapacity); if (updated == 0) { throw new BizException("课程名额不足或课程不存在"); }对应的SQL大致是这样:
UPDATE course_open SET selected_count = selected_count + #{delta} WHERE id = #{courseOpenId} AND selected_count + #{delta} <= #{maxCapacity}用影响行数来判断到底有没有占上名额,能避免并发选课时两个人同时读到剩余名额为1、然后都选上的问题。这里不需要复杂分布式锁,一条带条件的UPDATE语句就够了。
- 成绩录入权限:教师端查询可录入成绩的开课记录时,必须用当前登录教师的id去过滤,不能允许教师通过篡改参数给其他老师的课录成绩。
- 退课释放:退课时把选课状态改为1,同时执行
selected_count = selected_count - 1,注意这个操作和选课一样要放在事务里。
4. Spring Boot集成JAX-WS发布SOAP服务的完整过程
4.1 依赖引入与配置类写法
在Spring Boot工程里用CXF发布SOAP服务,第一步是引入CXF的Starter依赖,比手工配置CXF和Servlet要省事很多。以Spring Boot 2.7为例,在pom.xml中添加:
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.6.4</version> </dependency>然后写一个配置类,通过CXF的EndpointImpl把服务实现类发布到指定路径。这里一个容易踩的坑是:不要用JDK自带的javax.xml.ws.Endpoint.publish,那样会另起一个独立的HTTP端口,和Spring Boot的主服务脱离,后面处理统一端口、过滤器、全局异常都会很别扭。正确做法是用CXF的EndpointImpl。
import org.apache.cxf.Bus; import org.apache.cxf.jaxws.EndpointImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.xml.ws.Endpoint; @Configuration public class CxfWebServiceConfig { private final Bus bus; private final ScoreWebService scoreWebService; public CxfWebServiceConfig(Bus bus, ScoreWebService scoreWebService) { this.bus = bus; this.scoreWebService = scoreWebService; } @Bean public Endpoint scoreServiceEndpoint() { EndpointImpl endpoint = new EndpointImpl(bus, scoreWebService); // 发布路径:http://localhost:8080/services/scoreService endpoint.publish("/services/scoreService"); return endpoint; } }注意scoreWebService这里注入的是服务实现类的Bean,它的类上要标注@Service,而不是@Endpoint这一类的旧式注解。发布完之后,应用启动时控制台会看到CXF自动生成了WSDL路径,直接用浏览器访问/services/scoreService?wsdl就能看到XML格式的接口描述。
4.2 服务端接口与实现类的分层陷阱
SOAP服务的接口定义要走JAX-WS规范。接口上标注@WebService,方法上标注@WebMethod,参数和返回值上用@WebParam标记名称。返回值对象需要是一个普通的POJO,而且要能序列化为XML,字段上可以用@XmlElement控制节点名。这里有个非常容易犯的错误:直接把业务实体类返回出去。实体类里如果存在嵌套关联或者其他类型的字段,CXF生成WSDL时可能报错或生成一堆难以理解的复杂类型,建议单独定义DTO类,只暴露必要字段。
我以一个成绩查询服务为例。接口定义:
@WebService(targetNamespace = "http://service.course.demo/", name = "ScoreWebService", serviceName = "ScoreWebService") public interface ScoreWebService { @WebMethod ScoreResult getScoreByStudentNo(@WebParam(name = "studentNo") String studentNo, @WebParam(name = "semester") String semester); }实现类不要一上来就写数据库操作,而是继续调用内部的业务Service。这样做的目的很直接:底层事务、缓存、业务校验都收敛在Spring管理的Service里,Web Service层只做协议适配。实现类写法如下:
@Service public class ScoreWebServiceImpl implements ScoreWebService { private final ScoreBusinessService scoreBusinessService; public ScoreWebServiceImpl(ScoreBusinessService scoreBusinessService) { this.scoreBusinessService = scoreBusinessService; } @Override public ScoreResult getScoreByStudentNo(String studentNo, String semester) { return scoreBusinessService.queryScoreByStudentNo(studentNo, semester); } }4.3 WSDL生成与调用方wsimport测试
服务启动后,根据实际部署地址打开WSDL地址。如果你在前面的配置里发布到了/services/scoreService,那么本地开发环境的WSDL地址就是:
http://localhost:8080/services/scoreService?wsdl浏览器打开后是一大段XML,里面包含wsdl:service、wsdl:portType和xsd:element等结构。这就是WSDL的核心作用:它把服务能做什么、入参出参长什么样、用哪个HTTP地址访问,全部以机器可读的方式描述出来。在论文里截一张WSDL的关键片段,再配一段说明,就能很直观地体现“Web Service技术”不是空话。
客户端测试有两种方式。一种是用wsimport命令直接生成调用代码:
wsimport -keep -p com.demo.client http://localhost:8080/services/scoreService?wsdl执行后会在当前目录生成com/demo/client包下的若干类,其中有一个ScoreWebService接口和一个ScoreWebServiceImpl服务实现类。接着写个简单测试代码调通:
public class ScoreClientTest { public static void main(String[] args) { ScoreWebService service = new ScoreWebServiceService() .getScoreWebServicePort(); ScoreResult result = service.getScoreByStudentNo("20240001", "2024-2025-1"); System.out.println(result.getCourseName() + ":" + result.getScore()); } }另一种方式是直接在SOAP UI里导入WSDL,手动构造SOAP请求报文发送。两种方式二选一即可,但建议在答辩演示前用客户端代码方式跑通一次,因为现场环境如果没有安装SOAP UI类工具,命令行或者IDEA里的测试类更稳。
4.4 事务、异常和鉴权在Web Service层的处理
这里集中说三个容易让工程“能跑但经不起问”的点。
第一是事务。如果ScoreWebServiceImpl直接调用了多个DAO操作,务必要让被调用的业务Service方法标注@Transactional,并确保该Service是Spring容器管理的Bean。CXF发布SOAP端点时配置类注入的scoreWebService也是Spring Bean,所以Spring事务拦截器能够生效。这要求你在设计接口时不要通过new关键字创建服务对象,否则事务注解完全不生效。
第二是异常。不要让SQL异常或者RuntimeException直接穿透到SOAP调用方。CXF收到未处理异常时,生成的SOAP Fault内容可能异常粗糙,甚至把内部堆栈信息暴露出去。建议在Web Service实现类或业务Service里捕获业务异常,转换为自定义的Fault消息,或者抛出带有明确错误码的Fault异常。以CXF的org.apache.cxf.interceptor.Fault为例,可以用它包装统一的错误信息。
第三是鉴权。SOAP服务是对外发布的,如果谁都能拿学号查成绩,这系统就不用答辩了。最简单的方案是给每个对外方法加一个token参数,调用方在登录系统后通过内部接口获取token,调SOAP服务时把token传进来;服务端在交互层校验token是否有效、是否过期。想做得更正式,也可以用CXF的拦截器在请求进入前统一校验SOAP Header里的凭证信息,但毕设阶段在接口参数里带token的方式已经足够,且讲解起来容易听得懂。
5. 登录认证与权限控制:这种系统避不开RBAC
5.1 Session还是JWT的取舍
教务管理系统天然需要角色权限:学生能查课选课,教师能录成绩,管理员能维护基础数据。如果不区分角色,前面做的所有业务约束都可以被绕过。至于用Session还是JWT,要看你的页面形态:
| 方案 | 适合场景 | 实现难度 | 答辩亮点 |
|---|---|---|---|
| Session + 拦截器 | 后端模板渲染,前后端不分离 | 低 | 一般 |
| JWT + 认证拦截器 | 前后端分离,REST接口 | 中 | 较高 |
| Spring Security | 追求完整安全框架 | 高 | 较高但要能讲清楚配置逻辑 |
我的建议是优先选Session + 拦截器。毕业设计的时间大头应该花在业务逻辑上,而不是调安全框架的过滤器链。只要在配置类里注册一个拦截器,让它处理登录状态和角色判断,就能完成80%的需求。做JWT、Spring Security可以作为中期检查之后的加分扩展,但不要一开始就把主架构押在复杂的权限框架上。
5.2 拦截器与角色判断的实现思路
登录成功后将用户对象放入Session,key可以叫LOGIN_USER。拦截器负责两个判断:是否登录、当前角色是否允许访问某个路径。路径和角色的规则可以直接硬编码在拦截器里,毕设规模完全可控,没有必要设计动态权限表。
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser = request.getSession().getAttribute("LOGIN_USER"); if (loginUser == null) { response.sendRedirect(request.getContextPath() + "/login"); return false; } // 可在此处提取角色,进一步做路径级别的角色校验 return true; } }然后注册拦截器,并放行登录页、静态资源、SOAP服务路径:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") .excludePathPatterns( "/login", "/doLogin", "/css/**", "/js/**", "/img/**", "/services/**" ); } }把/services/**排除掉是必要的,因为SOAP服务不经过页面登录,它的鉴权由第4.4节的token机制处理。
角色判断通常在业务Service里做。比如教师录入成绩时,Service方法先取出当前登录教师id,再校验这个开课记录的teacher_id和当前id是否一致,不一致直接抛“无权操作”。不要把这种校验全部堆在Controller里,因为一个Controller方法被调用时上下文可能一致,但Service作为最后防线,把数据库层面的归属权校验放在它里面更安全。
5.3 密码安全与越权防护
密码存储必须用BCrypt加密。很多教程为了演示方便把密码明文入库,答辩时老师问一句“数据库泄露了怎么办”就会很难受。Spring Security里有一个单独的BCryptPasswordEncoder组件,不引入完整Spring Security也能单独使用,注册成一个Bean后,在注册和修改密码的地方调用。校验时用matches(原始密码, 加密密码)。
越权防护还要注意URL参数。典型问题出现在学生查询成绩的接口上:学生A请求接口时传入学生B的学号,如果后端只看参数而不回查当前登录用户的身份,就会查出别人的成绩。正确做法是查询操作优先以Session里的学生id为准,传入的学号只做页面展示和日志记录。数据范围权限是信息管理系统里比角色权限更容易出问题的地方,往往被忽略,一旦被发现就是大部室级事故。
6. 打包部署与答辩现场:能跑通只是第一步
6.1 从开发到演示:jar包部署与外置Tomcat的差异
系统开发完成后,建议打可执行jar包部署,这也是Spring Boot最推荐的模式。在pom.xml里确保packaging为jar,执行mvn clean package,然后在target目录拿到xxx.jar,命令行运行:
java -jar xxx.jar这种部署方式对答辩演示特别方便,把jar包拷到一台装了JDK8的机器,一条命令就能启动。如果答辩现场用的是学校机房电脑,可能没有外网下载依赖,所以提前在本地把包打全,再带上MySQL初始化脚本,整个环境迁移能控制在十分钟内。
如果老师明确要求必须部署到外置Tomcat,那就需要把packaging改为war,并将启动类继承SpringBootServletInitializer重写configure方法:
@SpringBootApplication public class CourseApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(CourseApplication.class); } }改了war方式之后,使用内置Tomcat启动时访问路径可能会带上context-path,注意统一配置,否则演示时页面链接会404。
6.2 答辩中最容易被追问的四个点
根据过往经验,这类题目答辩现场被问最多的问题集中在以下几类,提前准备好答复思路就好:
你的系统哪里体现了Web Service技术?回答要点:系统内部主体功能使用Spring MVC和JSON交互;对外提供基于SOAP协议的成绩查询服务,通过CXF发布,客户端通过WSDL发现并调用服务。尽量切到代码里展示WSDL文件和wsimport生成的客户端代码,比口头描述有说服力。
SOAP和REST有什么不同?回答要点:SOAP基于XML标准协议,依赖WSDL实现服务描述,适合跨语言、跨平台的企业级接口标准化;REST利用HTTP现有语义,通常返回JSON,轻量高效。强调你既使用REST处理内部页面请求,又用SOAP处理对外服务,两者是互补关系。
选课时并发怎么防超选?回答要点:用条件UPDATE实现原子操作,数据库行锁加唯一索引,防止同一门课并发下超选;展示相关SQL和影响行数判断逻辑。
如果学生退课了,名额如何回到可选项里?回答要点:退课和释放名额在同一个事务里完成,修改选课记录状态后对开课记录的已选人数减一,再开放该课的选课入口。
不用背答案,只要这几个问题能结合自己的代码讲清楚,老师基本就认可这是自己做的系统。
6.3 论文里该把Web Service技术写进哪一章
很多同学代码写得很顺,但论文里“技术介绍”部分写得很泛。针对有“web service技术”字样的题目,我建议论文按这个思路编排技术章节:
- 第三章“系统关键技术”拆成三节:Spring Boot框架、Web Service技术(SOAP协议、WSDL、JAX-WS规范、CXF框架)、MyBatis与MySQL。注意将理论概念和自己项目的技术选型结合起来写,不要大段粘贴教材。
- 第五章“系统详细设计与实现”里单独加一个小节,叫“基于Web Service的对外接口设计与实现”,里面贴出核心服务接口代码、CXF配置、WSDL局部片段以及客户端调用测试截图。
- 数据库设计部分重点描述选课表唯一索引和开课表容量字段,方便答辩时直接对应到并发方案。
论文里技术图不需要画得太庞大,但要在系统架构图上把“浏览器→Spring MVC”和“外部系统→CXF Web Service→业务Service”两条链路区分开,这样阅卷老师一眼就能看出架构层次。
最后分享一个细节:我在做类似项目时,会把WSDL文件保存一份到resources目录下手动备份,因为不同机器上CXF生成的WSDL动态生成过程偶尔会有出入,万一现场网络有问题或服务没起来,至少能在论文和PPT里展示同一个可靠版本。这个习惯不算什么技巧,但它能在答辩这样高压的场合多给你留一条退路。整个项目做下来,最有价值的部分其实不是某个炫酷的技术,而是把“教务管理”这种常见业务从需求到设计再到代码完整地走了一遍,这个过程比多少篇现成代码都值得写进自己的履历。