提到SSM这套组合做过Java Web开发的人应该都不陌生。Spring、SpringMVC、MyBatis三件套在国内中小型项目里的占有率一直很高尤其是学校课程设计、毕业设计和企业内部系统这类场景几乎成了标准答案。而“篮球CBA联赛信息管理系统”这个题目正好是把SSM框架落地的经典练习对象业务逻辑清晰、数据关联明确、功能边界好划分做出来的东西既能展示技术栈掌握程度又不会复杂到一个人搞不定的地步。这个系统要解决的核心问题很简单把CBA联赛从球队、球员到赛程、比分、积分榜的完整信息链用Web方式管理起来替代手工Excel记录和线下沟通。适合正在学Java Web的开发者、需要交课程设计的学生以及想了解SSM项目完整落地流程的初级工程师参考。我自己在做类似项目时踩过不少坑这篇就按实际开发顺序把从设计到部署的关键环节完整拆开讲一遍。1. 项目整体设计与技术选型思路1.1 为什么是SSM而不是Spring Boot现在很多人一上来就推荐Spring Boot但放到联赛信息管理系统这个场景里SSM反而更合适。原因不复杂这个项目的数据表关系多球队、球员、赛程、比分、积分之间都有强关联用MyBatis可以精确控制每一条SQL复杂查询写起来直观。另外课程设计和教学场景普遍还在讲SSM配套资料多遇到问题也容易搜到解决方案。SSM的核心分工是Spring管Bean和事务SpringMVC管请求分发MyBatis管数据库操作。三者各司其职边界清楚。我用生活化的方式给你打个比方Spring是公司的行政部负责所有对象的创建和调度SpringMVC是前台负责把客人的请求分给对应的部门MyBatis是仓库管理员专门负责从数据库仓库里取东西和存东西。三个角色配合一个完整的Web项目就跑起来了。1.2 功能模块划分与整体架构我做的这个CBA联赛信息管理系统功能划分参考了真实联赛管理平台的常见需求分前台展示和后台管理两大部分。前台面向普通访客提供联赛资讯、球队列表、球员信息、赛程比分、积分榜五大板块后台面向管理员提供球队管理、球员管理、赛程管理、比分录入、新闻发布、管理员账号管理六大功能。架构上采用经典的三层架构加SSM框架组合表现层用SpringMVC处理请求转发和数据绑定业务层用Spring管理Service组件和声明式事务持久层用MyBatis操作MySQL数据库。前端页面用JSP配合Bootstrap框架保证页面在不同分辨率下都有可用性。这个项目选型的好处在于每个层的职责单一改前端不影响后端逻辑换数据库不用动Java代码加功能只需要新增Mapper方法和Service方法。真实项目里最怕的就是逻辑揉在一起SSM的强约束恰好能避免这种问题。提示如果你是自己练手别急着加Redis、MQ这类中间件。联赛信息管理系统的并发量远没到需要引入这些的程度先专注把SSM本身的用法吃透这是最务实的做法。2. 数据库设计与核心业务建模2.1 主要数据表结构设计数据库设计是这类信息管理系统的地基。表结构设计不好后面写SQL和Java代码时处处难受。我做这个项目时规划了六张核心表球队表、球员表、赛程表、比分记录表、积分榜表也可用视图、管理员表。球队表team的字段设计比较直接CREATE TABLE team ( id int(11) NOT NULL AUTO_INCREMENT, team_name varchar(50) NOT NULL COMMENT 球队名称, team_city varchar(30) DEFAULT NULL COMMENT 所在城市, home_court varchar(100) DEFAULT NULL COMMENT 主场球馆, coach varchar(30) DEFAULT NULL COMMENT 主教练, founded_year int(4) DEFAULT NULL COMMENT 成立年份, team_logo varchar(200) DEFAULT NULL COMMENT 队标图片路径, created_time datetime DEFAULT CURRENT_TIMESTAMP, updated_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;球员表player是信息量最大的表。除了常规的姓名、年龄、身高、体重、位置还需要关联球队ID、球衣号码、国籍、选秀年份、职业生涯数据等字段。我的建议是关联字段用逻辑外键而不是物理外键也就是只存球队ID不设置FOREIGN KEY约束。这样做的原因是后期如果要做数据迁移或者批量导入物理外键容易成为瓶颈。靠Service层来保证数据完整性是SSM项目里的常见做法。2.2 赛程与比分的关联设计赛程表和比分表是这个系统的难点。我的设计思路是赛程表schedule记录一场比赛的基本信息比分作为独立字段存在赛程表里同时单独建一张比分明细表score_detail记录每节得分和最后比分。为什么要单独建比分明细表因为比赛是分四节打的球迷想看到的不只是最终比分还有每节得分这是篮球信息系统的刚需。赛程表里存一个最终比分的冗余字段方便列表页直接展示不用查明细表比分明细表存每节得分用于详情页的可视化展示。CREATE TABLE schedule ( id int(11) NOT NULL AUTO_INCREMENT, match_no varchar(20) DEFAULT NULL COMMENT 场次编号, home_team_id int(11) NOT NULL COMMENT 主队ID, away_team_id int(11) NOT NULL COMMENT 客队ID, match_date date NOT NULL COMMENT 比赛日期, match_time varchar(20) DEFAULT NULL COMMENT 开赛时间, match_round int(4) DEFAULT NULL COMMENT 轮次, home_score int(4) DEFAULT NULL COMMENT 主队总得分, away_score int(4) DEFAULT NULL COMMENT 客队总得分, status tinyint(1) DEFAULT 0 COMMENT 0未开始 1进行中 2已结束, season varchar(20) DEFAULT NULL COMMENT 赛季如2024-2025, PRIMARY KEY (id), KEY idx_home_team (home_team_id), KEY idx_away_team (away_team_id), KEY idx_match_date (match_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里用了三个索引分别在主队ID、客队ID和比赛日期上。原因是系统最频繁的查询就是按球队查赛程和按日期查比赛没有索引的话数据量上来后查询会明显变慢。2.3 积分榜的计算策略积分榜是这个项目中最能体现业务逻辑的部分也是面试官最爱问的点。CBA的积分规则是胜一场积2分负一场积1分弃权积0分。排名先看积分积分相同看相互胜负关系再看净胜分。我的实现方式是不单独建积分榜表每次查询时通过SQL动态计算。这样做的好处是比分更新后积分榜自动变化不会出现数据不一致的问题。查询SQL大致如下SELECT t.id AS team_id, t.team_name, SUM(CASE WHEN s.home_team_id t.id AND s.home_score s.away_score THEN 1 WHEN s.away_team_id t.id AND s.away_score s.home_score THEN 1 ELSE 0 END) AS wins, SUM(CASE WHEN s.home_team_id t.id AND s.home_score s.away_score THEN 1 WHEN s.away_team_id t.id AND s.away_score s.home_score THEN 1 ELSE 0 END) AS losses, COUNT(s.id) AS total_games, SUM(CASE WHEN s.home_team_id t.id AND s.home_score s.away_score THEN 2 WHEN s.away_team_id t.id AND s.away_score s.home_score THEN 2 ELSE 1 END) AS points FROM team t LEFT JOIN schedule s ON (s.home_team_id t.id OR s.away_team_id t.id) AND s.status 2 GROUP BY t.id ORDER BY points DESC, (wins * wins losses) ASC;这个SQL用一次LEFT JOIN就把所有球队的胜场、负场、总场次和积分全部算出来了。最开始我写的版本是每条球队记录查一次数据库18支球队就要查18次效率很差。后来改成单条JOIN查询性能提升非常明显。这也是MyBatis项目里常见的优化思路能用一条SQL解决的事情绝不用循环去查。3. 后端核心功能实现与关键代码拆解3.1 管理员登录与权限控制管理后台的登录认证我用的Session方案。用户输入账号密码后Controller层调Service校验通过后把管理员信息存进Session再用SpringMVC的拦截器HandlerInterceptor做登录状态校验和权限控制。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Admin admin (Admin) session.getAttribute(loginAdmin); if (admin null) { // 未登录跳转到登录页面 response.sendRedirect(request.getContextPath() /admin/login); return false; } return true; } }拦截器在SpringMVC配置文件中注册时要注意放行登录接口和前端静态资源否则CSS、JS、图片全被拦截页面样式会全丢。这是新手经常遇到的问题我一开始也在这个坑里卡了好久。注册拦截器的XML配置方式如下mvc:interceptors mvc:interceptor mvc:mapping path/admin/**/ mvc:exclude-mapping path/admin/login/ mvc:exclude-mapping path/admin/doLogin/ /mvc:interceptor /mvc:interceptors密码存储不要用明文。我用的是MD5加盐的方式具体做法是取用户名加上一个固定盐值字符串拼接后再做MD5加密。虽然MD5不是最安全的方案但在学习项目中够用而且面试时可以顺势讲出MD5存在碰撞风险生产环境推荐BCrypt这句话反而能加分。3.2 赛程管理与比分录入后台管理中管理员录入比分后会同时触发多个操作更新赛程状态和比分、写入比分明细表。为了保证这些操作要么全部成功要么全部失败必须在Service层加事务控制。Service public class ScheduleServiceImpl implements ScheduleService { Autowired private ScheduleMapper scheduleMapper; Autowired private ScoreDetailMapper scoreDetailMapper; Override Transactional(rollbackFor Exception.class) public void recordScore(Schedule schedule, ListScoreDetail scoreDetails) { // 1. 更新赛程表的总比分和状态 schedule.setStatus(2); scheduleMapper.updateByPrimaryKeySelective(schedule); // 2. 先删除旧的比分明细再插入新数据 scoreDetailMapper.deleteByScheduleId(schedule.getId()); for (ScoreDetail detail : scoreDetails) { detail.setScheduleId(schedule.getId()); scoreDetailMapper.insert(detail); } } }这里有一个很关键的细节比分修改功能要支持重新录入所以我在事务方法里先删掉旧明细再插入新明细而不是直接再做一次插入避免产生重复数据。这个先删后插的思路在做关联数据更新时非常常用。3.3 新闻资讯与球队球员管理联赛新闻模块相对独立功能上就是常见的增删改查。这里我想重点讲一下图片上传的处理。球队队标、球员照片、新闻封面图都需要上传如果用Base64直接存数据库数据量会非常大数据库性能会明显下降。我的做法是把图片保存到服务器本地指定目录数据库里只存图片的相对路径页面展示时通过虚拟路径映射访问。SpringMVC中配置文件上传解析器bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver property namemaxUploadSize value5242880/ property namedefaultEncoding valueUTF-8/ /bean上传文件的保存逻辑用UUID重命名文件名可以避免中文名乱码和重名覆盖问题public String saveUploadFile(MultipartFile file, String uploadDir) throws IOException { // 获取原始文件名的后缀 String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); // 用UUID生成新文件名避免重名 String newFilename UUID.randomUUID().toString().replaceAll(-, ) ext; File dir new File(uploadDir); if (!dir.exists()) { dir.mkdirs(); } file.transferTo(new File(dir, newFilename)); return newFilename; }使用UUID重命名是个非常实用的习惯。想象一下两个球员都叫张伟上传的照片如果都叫zhangwei.jpg后面就会互相覆盖。UUID唯一性可以彻底避免这个麻烦。3.4 球员数据统计的MyBatis实现球员的个人数据统计是CBA信息管理系统的一个重要功能点包括场均得分、场均篮板、场均助攻等指标的计算。这个模块我用MyBatis的动态SQL来做一个灵活的统计查询。球员每场比赛的技术统计表player_stats按球员和场次记录数据统计时按球员ID分组聚合。以下是MyBatis Mapper中的实现方式select idselectPlayerStats parameterTypemap resultTypemap SELECT p.id AS player_id, p.player_name, p.team_id, COUNT(ps.id) AS played_games, ROUND(SUM(ps.points) / COUNT(ps.id), 1) AS avg_points, ROUND(SUM(ps.rebounds) / COUNT(ps.id), 1) AS avg_rebounds, ROUND(SUM(ps.assists) / COUNT(ps.id), 1) AS avg_assists, ROUND(SUM(ps.steals) / COUNT(ps.id), 1) AS avg_steals, ROUND(SUM(ps.blocks) / COUNT(ps.id), 1) AS avg_blocks FROM player p LEFT JOIN player_stats ps ON p.id ps.player_id where if testteamId ! null AND p.team_id #{teamId} /if if testseason ! null AND ps.season #{season} /if /where GROUP BY p.id, p.player_name, p.team_id ORDER BY avg_points DESC /select这种写法支持按球队和赛季进行过滤同时用ROUND函数处理平均数的小数位数。动态SQL是MyBatis的核心优势之一如果条件有多重组合用标签拼SQL比在Java代码里拼字符串要安全得多也能有效避免SQL注入问题。4. 前端页面设计与异步交互实现4.1 JSP页面结构与响应式布局前端这一块我采用的方案是JSP加Bootstrap 4。JSP作为SSM项目的传统视图层最大的好处是能通过EL表达式和JSTL标签在后端渲染数据不需要额外搭建前后端分离环境。页面的总体布局分成三块顶部导航栏、中间主体内容区、底部版权信息栏。导航栏展示联赛Logo、球队列表、赛程、积分榜、新闻资讯等入口主体内容区通过JSP的include动作或iframe方式来组织。这里有一个经验分享列表页的数据量控制很重要。比如赛程列表页如果默认展示整个赛季所有比赛数据量会非常大页面加载会很慢。我给赛程列表加了分页和按轮次筛选的功能每页只显示10条记录配合一个轮次选择器体验好很多。分页用的PageHelper插件集成方式比较简单在MyBatis配置里加一个插件即可plugins plugin interceptorcom.github.pagehelper.PageInterceptor property namehelperDialect valuemysql/ property namereasonable valuetrue/ /plugin /pluginsPageHelper的reasonable参数很有用设置为true后如果我请求的页码超出实际范围它会自动把页码修正为1页或最后一页不会报错。4.2 Ajax异步实现比分刷新和详情查看赛程列表页需要一种体验更好的方式展示比分我在页面使用Ajax定时刷新的方式让比分信息可以动态更新。核心逻辑是这样的页面加载时先通过后端渲染出当日赛程框架然后每隔5秒钟发一次异步请求查询比赛状态和比分如果比赛已经结束就停止刷新该场比赛的比分。function loadMatchResult(matchId) { $.ajax({ url: /schedule/result?matchId matchId, type: GET, dataType: json, success: function(result) { if (result.success) { var data result.data; // 更新比分显示 $(#homeScore_ matchId).text(data.homeScore); $(#awayScore_ matchId).text(data.awayScore); // 如果比赛结束标记状态 if (data.status 2) { $(#status_ matchId).text(已结束); } } } }); }Controller层对应的接口返回JSON数据这个处理不返回视图直接通过ResponseBody注解返回对象SpringMVC会自动调用Jackson把对象转成JSON字符串。4.3 前端页面性能优化页面性能是很多初学者不会考虑但在真实项目中很重要的问题。这个联赛管理系统的首页包含多个数据模块如果按照常规方式逐个查询再渲染首页的加载时间会非常长。优化办法有两个第一个是合并CSS和JS文件减少HTTP请求数量。这是最基础但也最有效的优化手段。我用的Bootstrap、jQuery、自定义样式和脚本全部合并压缩成一个CSS和一个JS文件。第二个是给首页数据接口加一个简单的缓存。我用了Spring自带的缓存抽象在Service方法上加Cacheable注解给积分榜设置60秒的过期时间。这样60秒内的重复访问直接走缓存不用查数据库压力小很多。Cacheable(value standingsCache, key standings_ #season) public ListStandingsItem getStandings(String season) { // 查询数据库并计算积分榜 }虽然这只是一个简单应用但能提前感受一下缓存的使用场景。对后面理解Redis这类分布式缓存也有帮助。5. 部署流程与高频问题排查实录5.1 开发环境准备与项目初始化开发这个项目的环境清单如下软件版本说明JDK1.8SSM项目兼容性最好Maven3.6依赖管理MySQL5.7数据库Tomcat8.5Web容器IDEA2019IDE有一个细节要注意JDK版本选择。Java 8是SSM项目最成熟的运行环境如果你本机装的是JDK 17或更新版本要注意在pom.xml里配置Maven编译器版本否则编译时会报出源发行版和目标发行版不一致的错误。这个错误我见过太多次了就是maven编译插件默认用的版本和实际JDK版本对不上。properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties项目初始化时直接用Maven的webapp骨架创建然后手动在pom.xml中添加SSM相关依赖。核心依赖包括spring-webmvc、spring-jdbc、mybatis、mybatis-spring、mysql-connector-java、druid连接池、jackson-databind、jstl等。5.2 启动报错与解决方案快查表在实际开发和部署过程中遇到报错是家常便饭。我把最常遇到的几个问题整理成一个速查表方便你们对照排查现象根本原因解决方案启动时报ClassNotFoundjar包未正确引入或版本冲突检查pom.xml依赖是否完整Maven重新reimport请求时报404路径配置错误或Controller映射不对检查web.xml的 servlet 映射是否包含*.do或/检查注解路径数据乱码编码设置不统一统一使用UTF-8JSP页面、web.xml过滤器、数据库连接URL都改mybatis绑定异常Mapper接口和XML文件未对应检查Mapper接口包名和XML文件namespace是否一致连接池超时MySQL配置和连接池参数不合理检查数据库地址、账号、密码ping下数据库第一个ClassNotFound问题是最常见的。SSM涉及大量依赖Spring版本和MyBatis版本之间可能存在兼容性问题。我的建议是直接用一套经过验证的版本组合不要追求最新。比如Spring 5.1.x配MyBatis 3.5.x和mybatis-spring 2.0.x这个组合在稳定性和兼容性上经过大量项目验证基本不会出问题。5.3 跨域与调试技巧开发阶段前后端交互频繁虽然SSM项目通常不做前后端分离但调试接口时还是会遇到跨域问题。我在做这个项目时在Controller类上加了CrossOrigin注解来允许跨域请求。这个注解的作用是允许指定来源的跨域访问方便前端单独调试页面时不会因为同源策略被拦截。另外一个很实用的调试技巧是用浏览器的F12开发者工具看Network面板。接口返回什么状态码、返回数据长什么样一目了然。定位问题时先看Network再看Console能节省大量时间。比如前端页面白屏先看Network是接口挂了还是静态资源加载失败然后再做下一步排查不要上来就怀疑后端代码要顺着请求链路一层层查。5.4 部署到云服务器的注意事项项目开发完后面临部署问题。如果是部署到Tomcat需要注意war包的结构是否正确。用Maven打包时先执行clean再执行package确保打的包不包含旧文件。云端部署有两个容易踩的坑我单独说一下第一个是数据库连接问题。本地开发时数据库地址一般是localhost部署到服务器后要改成云数据库的内网或公网地址。同时MySQL的用户权限也要处理一下默认的root用户一般只允许本机访问需要新建一个允许远程连接的用户或者把root的host改成%。第二个是端口开放问题。Tomcat默认监听8080端口服务器安全组要放行8080端口外部访问。如果使用Nginx做反向代理把80端口的请求转发到8080端口还需要配置Nginx的转发规则。很多项目部署不上和代码没关系纯粹是安全组或防火墙规则没配好。6. 系统测试要点与性能优化经验6.1 功能测试重点路径梳理这类管理系统测试时重点不在于界面多好看而在于核心业务链路是否正确。我测试时一定会走通的场景有这些管理员登录后添加一支新球队再给新球队添加5名球员录入一场比赛的比分确认积分榜排名自动变化修改一场已经结束的比赛比分确认积分榜同步更新删除一个球员之后确认他所在球队的球员列表正常显示。第三条修改已结束比赛的比分是最容易出问题的地方。因为比分修改会直接影响积分榜事务处理不好就会出现积分表数据和赛程表数据不一致。测试用例里必须覆盖这个场景前后积分变化要能对得上。6.2 SQL性能优化与索引策略数据量小的时候SQL性能问题不明显。但是联赛系统如果积累了多个赛季的数据赛程表可能轻松上万条记录。这时候索引就变得非常重要了。我建立的索引策略是赛程表的球队ID字段和比赛日期字段建立联合索引比分明细表的赛程ID字段建立普通索引球员表的球队ID字段建立索引。这样能保证按球队查赛程、按日期查比赛、按场次查比分明细、按球队查球员这些高频查询都能命中索引。如何验证SQL是否走索引用EXPLAIN关键字看执行计划。我习惯在写完复杂查询后先用EXPLAIN跑一遍看到type列是ALL全表扫描就考虑加索引看到是ref或range就放心了。这个习惯坚持下来基本不会在生产环境遇到慢查询问题。6.3 Druid连接池配置与监控数据库连接池我用的Druid这算是国内Java项目的事实标准。Druid除了连接池的常规功能外还自带一个监控页面可以实时查看SQL执行情况、慢查询记录、连接池使用率等。Druid的基本配置如下bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/cba_db?useUnicodetrueamp;characterEncodingutf8/ property nameusername valueroot/ property namepassword value123456/ property nameinitialSize value5/ property nameminIdle value5/ property namemaxActive value20/ property namemaxWait value60000/ /bean有几个参数值得解释一下initialSize是初始化时建立的连接数minIdle是最小空闲连接数maxActive是最大活跃连接数maxWait是获取连接的超时时间。参数设置要根据实际并发量来定课程设计级别的项目maxActive设10到20就足够了设太大反而浪费数据库资源。配置好之后访问项目的/druid路径可以看到监控页面对了解项目的运行状态很有帮助。7. 从课程设计到简历项目的提炼思路7.1 项目亮点的提炼方法如果你做完这个项目后打算写进简历项目的技术描述很重要。同样一个项目不同写法给面试官的印象差别很大。有一个绝对不要去写的描述方式是使用SSM框架完成了CBA联赛信息管理系统的增删改查功能。增删改查这四个字一出来面试官就觉得这是个普通的练习项目。换一种表达角度就会好很多实现了用户权限控制通过SpringMVC拦截器对后台资源进行访问控制设计并实现了多表关联查询使用MyBatis动态SQL完成复杂条件筛选采用Druid连接池监控SQL执行情况通过索引优化将关键查询效率提升数倍。这个提法的转化思路就是把做了什么升级成如何做的以及解决了什么问题。哪怕功能本身很简单表达方式的不同也会直接影响面试官的第一判断。7.2 常见面试追问点梳理面试官在项目追问时高频会问Spring的IoC和AOP在这个项目中具体用在什么地方MyBatis接口绑定XML是怎么实现的SpringMVC的请求完整流程是怎样的事务失效的几种场景你踩过哪些举一个具体例子Spring的AOP在这个项目里最直接的应用就是Service层的事务管理。你给方法上标注Transactional注解Spring通过AOP机制在方法执行前开启事务、方法正常返回后提交事务、方法抛出异常后回滚事务。这个回答直接展现了你对事务和AOP结合的理解。还有SpringMVC的请求流程这个问题结合自己的项目讲会非常清晰请求先到达DispatcherServletHandlerMapping找到对应的Controller方法执行完返回ModelAndViewViewResolver解析成JSP页面渲染输出。把这个流程背清楚面试官会认为你真的用过而不是背概念。7.3 后续可以扩展的功能方向一个信息管理系统做完后不要止步后续扩展方向其实挺多的。我觉得最有性价比的三个扩展方向是加入ECharts数据可视化在球员数据详情页展示得分趋势折线图和命中率雷达图引入Redis做热点数据缓存把积分榜和球队排名这类查询量大的数据缓存起来改造用户体系增加球员注册和球迷账号体系让球员自己提交训练数据。这三个方向里数据可视化的投入产出比最高。ECharts是纯前端可视化库通过Ajax从后端拿JSON数据前端直接渲染图表不涉及复杂的后端改动但展示效果提升最明显。面试时讲到这个点也有东西能秀出来。8. 一段话做完这个项目的真实体会做这个CBA联赛信息管理系统我自己最大的收获是弄懂了SSM框架的协作机制。每天都会接触这些框架但真正把它们组合在一起做完一个完整项目之后才对Spring容器管理对象的生命周期、MyBatis动态SQL的灵活拼接、SpringMVC请求流转的全过程有了真正深入的理解。框架单独学是一回事组合在一个项目里应用到实际业务是另一回事。那些在博客和教程里看不懂的概念踩过坑之后再回来看其实自然而然就明白了。如果你也在做类似课程设计或练手项目我建议体量适中就好把每个功能做扎实、把每个报错弄清楚比做十个半成品都管用。做完之后你会发现后面再碰Spring Boot这种更现代的开发框架理解起来会顺利很多。
