SSM学生信息管理系统源码解析:从三层架构到MyBatis高级应用
简介这是一套基于SSMSpringSpringMVCMyBatis主流Java Web框架开发的学生信息管理系统完整源码面向Java初学者与Web开发入门者适用于课程设计、毕业设计及企业级轻量后台系统学习实践。系统采用前后端分离架构后端提供完整的CRUD接口与权限管理模块含管理员、教师、学生三类角色前端集成Vue实现交互界面涵盖学生档案、课程管理、成绩录入、班级维护等核心业务功能。压缩包共119个文件包含36个核心Java业务类如AdminController、STU、Grade等、31个XML配置与映射文件支撑Spring容器管理与MyBatis数据操作、38个编译后Class字节码以及PNG图标、Properties配置、README说明等辅助文件整体仅310KB结构精简、依赖清晰、开箱即用。已有4917人下载学习读者可直接导入IDE运行调试快速掌握SSM整合流程、分层架构设计思想及典型教育管理系统的工程组织方式。1. 这套SSM学生信息管理系统本质是一个三层架构的最佳练习样本先说结论如果你正在学Java Web或者刚把Spring、SpringMVC、MyBatis分别看了一遍但始终没串起来那这套基于SSM实现的学生信息管理系统源码就是最适合拿来对照着写的练手项目。它不做作不花哨所有代码都围绕学生信息管理这个常见业务场景展开却又把三层架构该有的东西全包含进去了表现层负责接收请求和返回视图业务层封装具体的增删改查逻辑持久层通过MyBatis与MySQL打交道。很多初学者拿到这种源码第一反应是打开IDE直接跑。但我的建议正好相反先别急着启动把它当一份结构地图来看。学会看包结构比学会运行更重要。1.1 系统模块划分与功能清单这套系统的功能划分非常典型。从使用角色来看主要分成管理员端和学生端。管理员端管理学生基础信息、课程信息、成绩录入与维护、用户账号管理学生端则更多是查询自己的信息、查看课程列表和自己各科成绩。权限边界清晰两个角色对应的Controller、Service、Mapper基本是平行扩展的这也是我推荐初学者先看它的原因——你能清楚看到同一个业务功能在Controller层、Service层、Mapper层分别长什么样。从功能清单上可以拆成这么几个核心模块学生信息管理新增学生、编辑学生、删除学生、按学号/姓名/班级组合查询课程信息管理课程新增、课程列表、课程状态维护成绩管理录入成绩、修改成绩、按学号和课程维度查询成绩列表登录与权限管理员/学生两种角色的登录认证、Session校验、未登录拦截。这五个模块基本覆盖了大多数后台管理系统的通用操作也就是说你把这套源码啃透了换一个图书管理订单管理之类的项目底层骨架几乎能直接平移。关键不在于功能多复杂而在于它是最小的完整闭环——有登录、有表关联、有增删改查、有列表分页学完能形成完整认知。1.2 这套源码适合谁来参考我遇到过好几类人来问这套源码有刚毕业准备找工作、需要拿项目经验应付面试的应届生有已经在做Spring Boot、但早期SSM基础不扎实想回头补课的后端开发还有学校课程设计直接要求用SSM做学生管理系统的学生。三类人群的目标不一样但最终都能从这套源码里找到自己想要的东西。如果是准备面试重点看三块Spring容器是如何把Controller、Service、Mapper串起来的SpringMVC拦截器在登录权限里怎么生效的MyBatis的动态SQL和参数绑定细节。这是SSM面试题最高频的三块区域。如果是准备课程设计重点看数据库表设计和页面跳转逻辑你会发现整个项目就是表驱动页面的产物——页面上每个列表、每个表单背后都对应一张表和一组CRUD方法。如果是做技术补课重点看两个XML文件的配置分工以及Tomcat部署的完整链路。理解了这两块SSM对你来说就不再是三个框架的名字而是一套能说清请求流程的完整技术栈。2. SSM整合核心两大Spring配置文件的分工与配置顺序SSM和Spring Boot最大的不同在于一切都需要你手动配置。这套源码里最关键的就是两个配置文件——applicationContext.xml和spring-mvc.xml。很多初学者跑不起来项目不是代码写错了而是配置文件里少了某个声明或者两个配置文件职责分错了。2.1 版本选型与依赖引入先看依赖。这套源码使用的核心版本大致是Spring 5.x、SpringMVC 5.x和Spring同版本、MyBatis 3.5.x、MyBatis-Spring整合包2.0.x加上MySQL 5.7/8.0的JDBC驱动、Druid或C3P0连接池、JSP和JSTL依赖。这里有一个非常容易踩的版本坑Spring 6 和 Spring 5 的包名有差异如果你把Spring版本直接升到6.x很多Javax开头的包比如javax.servlet、javax.annotation就要换成Jakarta开头而多数旧课程设计里的代码和依赖还是Javax体系的。所以我的建议是跑这套源码就用Spring 5.x为主别盲目追求最新版。依赖引入方面Maven的pom.xml里需要特别留意的是spring-webmvc依赖要显式添加。很多新人只加了spring-context导致前端控制器DispatcherServlet找不到类。另外MyBatis的依赖除了mybatis本身还需要mybatis-spring整合包否则Spring容器无法管理Mapper接口。2.2 applicationContext.xml数据源、SqlSessionFactory、事务applicationContext.xml是Spring的根容器配置它只配不涉及Web表现层的Bean。这套源码里它主要负责三件事加载数据源、创建SqlSessionFactory、配置事务管理器并开启事务注解支持。数据源可以用Druid。配置时要注意driverClassName、url、username、password这四项必须和本地MySQL环境对应。尤其url里要加上useUnicodetruecharacterEncodingutf8参数否则插入中文数据会乱码。这一点务必检查中文乱码在SSM项目中90%以上是这里漏配或不完整导致的。bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/student_db?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword valueroot/ /bean再就是SqlSessionFactory的配置。这里有一个关键点mapperLocations属性要指向Mapper XML文件所在的目录。这套源码里通常是classpath:mapper/*.xml如果你把XML文件放错位置比如放进了java目录而不是resources目录MyBatis在启动时就会报Invalid bound statement (not found)。事务部分使用DataSourceTransactionManager并开启tx:annotation-driven/这样Service层上的Transactional注解才生效。记住事务只控制Service层不控制Controller层这是程序设计上的一条铁律。2.3 spring-mvc.xml包扫描、视图解析器、静态资源放行spring-mvc.xml只负责Web表现层相关的Bean这里也特别容易和applicationContext.xml的包扫描范围弄混。正确做法是spring-mvc.xml只扫Controller注解的包applicationContext.xml扫Service、Mapper这层两个容器各管各的。视图解析器配置也很典型内部资源视图解析器InternalResourceViewResolver会将Controller返回的逻辑视图名拼接成真实JSP路径。比如Controller返回student/list它会映射到/WEB-INF/views/student/list.jsp。重点来了静态资源放行。DispatcherServlet默认拦截/意味着css、js、图片这些静态资源也会被它拦截从而404。如果没有加下面这段配置页面样式一定加载不出来mvc:default-servlet-handler/ mvc:annotation-driven/default-servlet-handler会让SpringMVC把静态资源请求转交给默认的Servlet处理器而annotation-driven负责注册RequestMappingHandlerMapping等组件。这两行几乎必须同时出现缺一不可。3. 学生、课程、成绩三大核心表的设计思路与MyBatis映射3.1 关系型表结构如何支撑业务这套系统的数据库表设计遵循很规范的第二范式2NF。主要包含这几张表学生表student、课程表course、成绩表score、用户表user。学生表的核心字段是学号stu_no、姓名name、性别gender、班级class_name、入学日期create_time。学号是业务主键也是学生登录账号关联的依据。课程表字段是课程编号、课程名、学分、授课教师。成绩表则通过stu_no和course_id两个外键关联到学生表和课程表再加上成绩数score。用户表保存登录账号和密码同时用一个role字段区分管理员还是学生。这里值得思考的是为什么成绩表要单独拆出来而不是在学生表里直接加一列。答案很简单一个学生可以选多门课一门课也有多个学生这是典型的多对多关系。多对多关系必须用中间表来承接于是有了成绩表。这个设计决定了很多业务查询都要走表关联于是就有了3.3里要说的多表联查。3.2 mapper接口与XML映射文件的对应关系MyBatis的Mapper层是接口与XML文件成对出现的。接口里写的抽象方法名必须和XML文件里某个SQL语句的id一致方法的参数类型要和parameterType兼容返回值要和resultType或resultMap匹配。 这是MyBatis动态代理机制要求的任何一处不一致启动时虽然不报错运行时调用就抛异常。这套源码里StudentMapper.java里定义了insertStudent、updateStudent、deleteStudent、selectStudentByNo、selectStudentList等方法然后在StudentMapper.xml里一一对应。建议看源码时先拿接口方法去XML里找对应id梳理一遍比读任何书都直观。3.3 多表联查resultMap的使用场景成绩列表需要展示学号、姓名、课程名、成绩但score表只有stu_no和course_id没有姓名字段。这就必须从student表和course表关联查询。MyBatis里做多表关联有两种风格可选一种是SQL里直接写JOIN然后映射到一个自定义的POJO另一种是使用resultMap的association或collection嵌套映射。我个人建议在这套学生管理系统里多用前一种做法写清晰的多表JOIN SQL再用resultMap来定义字段之间的映射。比如:resultMap idscoreDetailMap typecom.example.entity.ScoreDetail id propertyid columnid/ result propertystuNo columnstu_no/ result propertystudentName columnname/ result propertycourseName columncourse_name/ result propertyscore columnscore/ /resultMapresultMap的好处是当数据库字段和Java属性命名不一致时如stu_no和stuNo通过column和property手动对齐比依赖自动驼峰转换更可控尤其当SQL里出现表别名导致字段名冲突时resultMap可以准确指定每个字段的归属。4. 登录拦截与权限控制SpringMVC拦截器在源码里的实际配置4.1 拦截器的注册与拦截路径规则登录权限是这套系统里最典型的SpringMVC拦截器应用。在spring-mvc.xml中通过mvc:interceptors标签来注册拦截器mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/css/**/ mvc:exclude-mapping path/js/**/ mvc:exclude-mapping path/images/**/ mvc:exclude-mapping path/validateCode/ /mvc:interceptor /mvc:interceptors/**表示拦截所有请求/login和静态资源路径则放行。注意静态资源必须在这里再次放行即使你之前配了default-servlet-handler也不行因为拦截器是作用在请求链路更上层的它会先于静态资源处理器执行。如果不把/css/**和/js/**排除掉你会发现登录页能打开但页面样式和验证码图片全是坏的。4.2 实现HandlerInterceptor的核心逻辑拦截器类实现HandlerInterceptor接口最核心的是preHandle方法。这个方法在Controller方法执行前被调用返回true表示放行返回false表示拦截。在登录拦截场景里逻辑是这样的从Session中取出用户对象如果存在说明已经登录放行不存在就拦截重定向到登录页。但要注意这个逻辑必须排除当前请求本身就是访问登录页或登录接口的情况这就是4.1中exclude-mapping存在的意义。public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; }这里有个容易被忽略的细节request.getContextPath()一定要加。很多人写response.sendRedirect(/login)项目部署在Tomcat的根路径下没问题但如果你把项目包名改成/student部署这个/login就会跳到根路径下的/login直接404。用getContextPath()动态拼接才不管你部署在哪个路径下。4.3 按角色做细粒度控制登录成功只是第一步更细致的权限控制是区分管理员和学生。这套源码里通常的做法是登录时在Session中存用户对象同时存一个role属性在需要管理员才能执行的操作比如成绩录入、学生删除上再通过拦截器或Controller方法内判断角色。我更推荐在Service层做权限校验而不是Controller层。原因是如果同一个Service方法被多个Controller调用权限校验写在Service层能够保证不会有遗漏。另外在拦截器里做角色判断对页面级权限是够用的但涉及操作级权限比如普通学生不能删除学生记录在Service层里校验更稳妥。这套源码最值得借鉴的一点就是它在两处都做了处理页面访问靠拦截器兜底敏感操作靠Service方法内校验兜底形成了双保险。5. MyBatis高级用法在成绩查询模块中的落地5.1 动态SQL实现多条件组合查询学生信息管理系统的列表页几乎都有多条件查询的需求按学号查、按姓名模糊查、按班级查三个条件可能任意组合也可能一个都不填。在早期用JDBC写时代这要靠三层if判断手动拼SQL容易出错还容易被注入攻击。动态SQL就是来解决这个问题的。MyBatis的where标签配合if标签能优雅处理这种条件不固定的查询select idselectStudentList parameterTypemap resultMapstudentResultMap SELECT * FROM student where if teststuNo ! null and stuNo ! AND stu_no #{stuNo} /if if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testclassName ! null and className ! AND class_name #{className} /if /where /selectwhere标签会帮你处理一个经典问题当所有条件都不成立时它不会生成WHERE关键字当第一个条件是AND开头时它会自动去掉这个多余的AND。这就是为什么我不用手动写where 11这种丑陋的拼接方式。有一个细节值得注意SQL里我用的是LIKE CONCAT(%, #{name}, %)而不是LIKE %${name}%。二者区别很大我在5.3会详细说。5.2 foreach实现批量插入与学生批量删除管理后台经常需要批量操作这套系统里也涉及两个典型场景批量录入学生、批量删除学生。MyBatis的foreach标签就是为这种场景准备的。批量删除的写法delete iddeleteStudents parameterTypelist DELETE FROM student WHERE stu_no IN foreach collectionlist itemstuNo open( separator, close) #{stuNo} /foreach /delete批量插入的写法insert idinsertStudents parameterTypelist INSERT INTO student(stu_no, name, gender, class_name) VALUES foreach collectionlist itemitem separator, (#{item.stuNo}, #{item.name}, #{item.gender}, #{item.className}) /foreach /insert用foreach做批量插入时要特别注意MySQL的max_allowed_packet参数限制。如果一次插入的数据量太大比如几千条这个参数默认值可能会限制单条SQL的大小导致报错PacketTooBigException。生产环境一般建议分批插入比如每500条一批而不是一次性全塞进去。这套源码里如果数据量小直接一次性插入没问题你二次开发时如果导入Excel数据就得考虑分批处理。5.3 #{}与${}的区别以及用错之后的真实后果这是MyBatis面试题里被问烂了的问题但在源码里看会更有实感。#{}是预编译参数占位符MyBatis会把它替换成?然后通过JDBC的PreparedStatement传参这个过程天然防止SQL注入。${}是纯字符串替换直接把参数值拼接进SQL语句存在被注入改写SQL的风险。举一个具体例子。如果查询条件是ORDER BY ${sortField}因为排序字段这个位置无法使用?占位符所以只能用${}这时必须对sortField的值做白名单校验只允许传入stu_no、name、create_time等固定值。而条件查询的name值绝对不能用${}直接拼进WHERE子句——如果有人传一个1 OR 11你的SQL就变成查全表了后果非常严重。所以我的原则是能用#{}绝不用${}只有少数没法走占位符的位置表名、排序字段才用${}且必须对传入值做严格安全限制。这套源码里你仔细看正规的查询条件全是#{}只有极少数动态排序场景才用${}这才是合格工程的写法。6. 从Controller到Mapper的完整代码链路拆解6.1 Controller层返回值与页面跳转这套源码的Controller层分成多个类职责单一。比如StudentController只管学生信息的请求ScoreController只管成绩的请求。每个方法上面的RequestMapping定义访问路径方法参数上使用RequestParam获取表单参数返回String类型逻辑视图名给视图解析器解析。最典型的列表查询方法思路是这样的接收页面提交的条件参数调用Service层查询学生列表把列表放进Model返回student/list视图名。SpringMVC会把Model里的数据通过request.setAttribute暴露给JSP页面所以你在JSP里可以用${list}直接遍历。有一点容易被初学者忽略Controller一定要写上Controller注解并且spring-mvc.xml的包扫描要能扫到它。很多时候项目404是因为页面前端请求路径写错但更隐蔽的问题是Controller类的注解没生效Spring容器里压根没有这个Bean请求打进DispatcherServlet后找不到对应的HandlerMapping直接报404。6.2 Service层的Transactional事务控制这套系统的Service层接口加实现类的设计方式是SSM项目里最常见的一种规范写法。StudentService接口定义方法StudentServiceImpl实现类用Service注解注册为Spring Bean并通过Autowired或Resource注入Mapper接口。注意MyBatis的Mapper接口注入时Spring容器底层用的是动态代理来创建实现类所以你可能看不到StudentMapper的实现类代码但它确实存在于Spring容器中。这个机制理解起来有点绕但它是SpringMyBatis整合的精髓所在。在涉及写入操作的方法上比如添加学生、修改成绩要加上Transactional注解。这个注解的意义是如果方法执行过程中出现异常事务回滚数据库不会出现插了一半数据的情况。比如批量添加学生时第3条数据因为学号重复插入失败如果没有事务前2条就会残留数据库出现脏数据有了事务3条全部回滚不会留一半。不过我在这里要提醒一点Transactional只对RuntimeException运行时异常生效对受检异常Checked Exception默认是不回滚的。如果你在Service里try-catch捕获了异常并吞掉事务同样无法回滚。这个细节是很多线上事故的根源。6.3 前端参数传递与中文乱码处理前端JSP页面通过表单POST提交数据给Controller这里有一个SSM项目的老大难问题中文乱码。乱码通常是两个环节叠加导致的数据库连接URL没指定characterEncodingutf8前面2.2已经提过以及SpringMVC的CharacterEncodingFilter过滤器没配置。web.xml中的过滤器配置如下filter filter-nameencodingFilter/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-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mappingforceEncoding设为true的意义是无论请求还是响应都强制使用UTF-8编码。如果只设置encoding而不设forceEncoding它只对请求解码有效响应时的编码未必统一。建议直接把forceEncoding设为true一劳永逸。另外一个常见乱码源是Tomcat的GET请求编码。Tomcat 8及以上版本默认使用UTF-8解码URI所以GET请求参数一般不会有问题但如果你用的是Tomcat 7需要在server.xml的Connector上加URIEncodingUTF-8否则GET请求的路径参数中文也会乱码。7. 源码部署与二次开发的避坑指南7.1 IDEA/Tomcat部署的完整步骤拿到这套源码压缩包之后正确启动姿势是这样的先用IDEA以Maven项目方式导入源码等待依赖下载完成然后配置本地的Maven仓库镜像建议使用阿里云镜像否则首次下载Spring依赖很可能半天都下不完接着修改applicationContext.xml里的数据库连接信息对应你自己的MySQL账号密码最后用IDEA配置Tomcat将项目以war exploded方式发布到Tomcat运行。有一个很典型的部署报错我遇到过很多次ClassNotFoundException: org.springframework.web.context.ContextLoaderListener。这个错误的根本原因是web.xml里配置的Spring监听器导入的类在Tomcat中找不到通常是因为spring-web这个依赖没有打包进WEB-INF/lib。在IDEA中排查方法很简单打开Project Structure的Artifacts看Output Layout里WEB-INF/lib下有没有一个spring-web-xxx.jar没有就补进去问题立刻解决。7.2 静态资源404和登录页样式丢失问题部署完成后打开登录页最常见的就是页面有输入框但没有样式或者验证码图片显示不出来。用浏览器F12看Network你会发现一堆.css、.js请求全部返回404。这个问题我在4.1已经提到拦截器把静态资源也拦住了或者spring-mvc.xml里漏了mvc:default-servlet-handler/。另一种情况是JSP页面里引入静态资源的路径写成了/css/style.css但项目部署的上下文路径不是根路径而是/student于是资源实际路径应该是/student/css/style.css。很多老项目的JSP模板会写c:set varctx value${pageContext.request.contextPath}/然后在引用资源时用${ctx}/css/style.css动态拼接。如果你的项目中页面里都是硬编码的/css/xxx.css风格路径那部署到非根上下文路径后样式必然丢。建议全部改成EL表达式拼接上下文路径的方式一劳永逸。7.3 日期格式、分页查询与常见异常复盘最后集中整理几个我在这套系统二次开发中经常遇到的问题。日期格式问题。学生表里的入学日期create_time前端表单提交的是2024-09-01这种字符串而Java实体类的属性是Date类型。SpringMVC默认只能把带时间的完整格式yyyy/MM/dd HH:mm:ss转换成功单独的日期字符串会报400 Bad Request。解决方案是在表单对应字段上增加DateTimeFormat(pattern yyyy-MM-dd)注解或者在Controller类上配置一个全局InitBinder方法做日期转换。在Spring Boot项目里可以用JsonFormat但在SSM项目里最稳妥的还是在POJO字段上用DateTimeFormat它同时兼容表单参数绑定和JSON反序列化场景。分页查询问题。这套系统如果自己实现了分页通常是传两个参数pageNum和pageSize然后在SQL里用LIMIT #{start}, #{pageSize}计算起始位置start (pageNum - 1) * pageSize在Java代码里先算好。而在稍微大一点的项目里大家会更倾向使用PageHelper插件只要在Service方法后面一行PageHelper.startPage(pageNum, pageSize)MyBatis就会自动拦截并生成分页SQL。但PageHelper有个经典坑startPage调用后必须紧跟第一条查询语句如果中间隔着任何非查询逻辑分页拦截就可能失败或串到别的查询上。看源码时如果里面用到了PageHelper你可以专门检查一下这条规则。MyBatis缓存问题。在成绩查询和热门课程统计这一类的场景中MyBatis的一级缓存默认在同一个SqlSession中生效但如果使用了Spring整合每次Mapper调用都可能新建一个SqlSession一级缓存基本失效。二级缓存如果开启了会跨SqlSession生效但会给增删改操作带来缓存脏读风险。我在生产环境中通常建议绝大部分学生信息管理系统根本不需要开二级缓存它只会在你修改数据后给你带来数据没变的假象。还有一个运行期很头疼的异常MyBatis System Exception: Parameter xxx not found. Available parameters are [...]。我专门复盘一下这个问题的排查链路。你无法通过直接看SQL发现问题需要看Mapper接口那个方法的参数写法。如果方法签名的参数是(Param(stuNo) String stuNo, Param(name) String name)XML里用#{stuNo}#{name}是没问题的但如果方法参数没有Param注解而且传的是多个参数MyBatis就无法自动识别参数名除非开启了-parameters编译参数运行时就报参数找不到。这个错误的排查顺序是先看接口方法有没有Param注解再看XML里的参数引用名是否和注解值一致最后看resultType里的属性名是否和表字段列名对应。三步走完95%的参数绑定问题都解决了。8. 把这套源码跑通之后下一步该做什么如果你按照前面的配置和路径把这套学生信息管理系统源码完整跑通了那我很确定SSM这套技术栈在你这里已经有了一个完整的、能跑的通路认知。这时候我建议你做的不是马上扔掉学Spring Boot而是基于这套源码做一次小而关键的改造。我自己带过的新人里做这三次改造的人最后对SSM的理解深度明显不一样。第一次改造把登录拦截器的逻辑改成基于注解的权限控制。定义一个RequireRole(admin)注解在需要管理员权限的Controller方法上加注解然后在拦截器里通过反射读取HandlerMethod的方法注解做角色判断。这个改造做完你会彻底理解拦截器反射在Web框架里是怎么协同工作的以后看Spring Security的注解式权限控制会非常顺。第二次改造把学生列表的多条件查询改成使用PageHelper分页并在页面显示总记录数和页码条。这个改造会让你理解分页插件原理也会让你踩一遍startPage到底应该放在哪一行的坑踩完你就永远记住了。第三次改造把成绩查询模块从嵌套SQL改成关联查询并用resultMap的association标签完成多表映射。这个改造做完你对MyBatis处理表关联的核心机制就完全吃透了。三次改造做完这套源码才真正从别人的项目变成你自己的项目。之后你再去看Spring Boot体系会发现大量设计思想都能和这套源码里的配置一一对应上——Spring Boot只是帮你把那些繁琐的XML配置自动化了底层的请求流程、事务机制、Mapper代理模式一点都没变。把SSM的三层拆解能力练扎实再去学任何新的Java Web技术栈都不会觉得陌生这才是这套老技术栈里的源码留给你真正的价值。本文还有配套的精品资源点击获取