ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SSM框架整合:从零搭建Spring+SpringMVC+MyBatis开发环境

2026/9/24 23:44:58 拓冰建站 浏览量
SSM框架整合:从零搭建Spring+SpringMVC+MyBatis开发环境 2. 核心实现从零搭建springspringmvcmybatis开发环境2.1 JDK、Tomcat与IDE版本搭配这里先把最基础的开发环境铺好。很多人第一次做ssm项目一上来就在环境上踩坑JDK版本和Tomcat不兼容、Maven仓库下载慢、IDEA插件装不上……我直接给一套经过验证的组合JDK 1.8目前ssm框架最稳的版本不要用11、17去跑Spring 5.x以下的配置会有很多兼容问题Tomcat 8.5Servlet 3.1配合Spring MVC的注解驱动非常合适Maven 3.6.3管理jar包依赖比手动拷贝lib目录省太多事IDEA 2022.1以上社区版也够用关键是装好Lombok插件如果你在实体类里用了Data注解没有插件会直接编译报错选JDK 1.8而不是更高版本核心原因是ssm框架里的CGLIB代理、动态代理机制在高版本JDK下会出现反射相关的警告甚至报错尤其是Spring 4.x配JDK 11经常遇到java.lang.IllegalAccessError。为了保证不出幺蛾子1.8是几乎所有ssm课程的默认基线。2.2 Maven依赖的坑与版本锁定ssm项目的pom.xml看起来简单真正操作起来最容易出问题的就是依赖版本冲突。比如Spring的各个模块版本不一致会导致启动时出现BeanCreationException又比如mybatis-spring版本和mybatis版本不匹配会出现MapperFactoryBean相关的NoSuchMethodError。建议直接锁定一套版本组我实测下来这个组合最稳依赖版本说明spring-webmvc / spring-jdbc / spring-tx5.3.30全家桶统一版本不要混用mybatis3.5.15mapper代理模式的基础mybatis-spring2.1.2与mybatis 3.5.x配合无冲突mysql-connector-java8.0.33注意driverClassName要写对druid1.2.20数据库连接池jackson-databind2.15.2处理JSON返回lombok1.18.30简化实体类代码有人在pom里把spring-webmvc写成了5.2.x但spring-jdbc却用了5.3.x启动时不会立刻报错等到扫描Bean、加载事务管理器时才给你来个NoSuchMethodError定位起来非常痛苦。所以版本统一这件事一定要在写pom的时候一次性搞定。2.3 配置文件三大件jdbc.properties、applicationContext.xml、spring-mvc.xmlssm框架本质上是三个框架的整合配置文件也主要分三块但实际项目中经常有人把三层配置全塞进一个xml结果越写越乱。我习惯拆成三个文件通过web.xml统一加载逻辑非常清晰。先看jdbc.properties这就是最容易被忽视的小文件jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/school_community?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你自己的密码注意两点MySQL 8.0以上必须写com.mysql.cj.jdbc.Driver老版的com.mysql.jdbc.Driver会直接提示ClassNotFoundurl里必须带serverTimezone否则报时区错误。还要注意characterEncodingutf8这个不写中文数据存储和查询就会乱码尤其做校园交流平台这种充满帖子标题、用户昵称的场景中文乱码会让人崩溃。然后是applicationContext.xml它负责Spring核心容器、数据源、事务管理、MyBatis整合!-- 开启注解扫描Controller除外Controller交给SpringMVC管理 -- context:component-scan base-packagecom.schoolcommunity context:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan !-- 加载数据库配置 -- context:property-placeholder locationclasspath:jdbc.properties/ !-- Druid数据源 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ !-- 初始化连接数连接池最大数最小空闲数根据实际并发调节 -- property nameinitialSize value5/ property namemaxActive value50/ property nameminIdle value5/ /bean !-- SqlSessionFactory整合MyBatis -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ property nametypeAliasesPackage valuecom.schoolcommunity.entity/ !-- 开启驼峰映射数据库下划线字段自动转Java驼峰属性 -- property nameconfiguration bean classorg.apache.ibatis.session.Configuration property namemapUnderscoreToCamelCase valuetrue/ /bean /property /bean !-- Mapper接口扫描 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.schoolcommunity.mapper/ property namesqlSessionFactoryBeanName valuesqlSessionFactory/ /bean !-- 事务管理器 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/这个配置里有两个细节值得展开。第一component-scan要排除Controller否则Spring和SpringMVC两个容器都会创建Controller实例导致事务和AOP拦截混乱第二mapperLocations指向classpath:mapper/*.xml很多人把Mapper XML文件放在src/main/java里结果Maven默认不会把xml文件编译到classes目录导致启动时找不到SQL语句。解决办法是在pom.xml里加一段resources配置或者直接把xml放在src/main/resources/mapper下。2.4 SpringMVC配置与中文乱码过滤器spring-mvc.xml负责Controller扫描、注解驱动、视图解析器、静态资源放行、JSON消息转换!-- 开启SpringMVC注解驱动 -- mvc:annotation-driven/ !-- Controller包扫描 -- context:component-scan base-packagecom.schoolcommunity.controller/ !-- 视图解析器配置前后缀 -- bean idviewResolver classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean !-- 静态资源放行不然css、js会被拦截器拦掉 -- mvc:resources mapping/static/** location/static//还有web.xml里的编码过滤器这个必须配置在所有过滤器最前面否则POST提交的中文乱码问题会伴随你整个开发过程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-mapping3. 数据库设计与初始化3.1 校园交流平台的核心表结构一个交流平台的核心业务就三件事用户、帖子、评论。围绕这三件事最少需要5张表用户表、帖子表、评论表、帖子分类表、用户关注表。先看用户表CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码MD5加密后存储, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像URL, major varchar(100) DEFAULT NULL COMMENT 专业信息, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;密码不要明文存储项目里虽然演示但这是课程设计的评分点之一。用MD5加一个固定salt串比如md5(password schoolcommunity)虽然MD5不算安全但作为课程设计已经足够体现意识。可以在Java层用DigestUtils.md5DigestAsHex来处理。帖子表CREATE TABLE post ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL COMMENT 发布者ID, category_id int(11) DEFAULT NULL COMMENT 分类ID, title varchar(200) NOT NULL COMMENT 标题, content text COMMENT 内容, view_count int(11) DEFAULT 0 COMMENT 浏览量, like_count int(11) DEFAULT 0 COMMENT 点赞数, comment_count int(11) DEFAULT 0 COMMENT 评论数, is_top tinyint(1) DEFAULT 0 COMMENT 是否置顶, status tinyint(1) DEFAULT 1 COMMENT 状态1正常0删除, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_category_id (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT帖子表;3.2 索引设计与连表查询优化很多人做课程设计表一建就开始写代码根本不考虑索引。但交流平台首页要展示帖子列表必然要连user表查昵称和头像。如果数据量超过1000条没有索引的全表扫描速度会明显下降。我在user_id、category_id上各建了一个索引这是最基础的优化手段也是可以在论文或验收时讲出口的设计理由。首页帖子列表的查询SQL大概是这样的SELECT p.id, p.title, p.view_count, p.like_count, p.comment_count, p.create_time, u.nickname, u.avatar FROM post p INNER JOIN user u ON p.user_id u.id WHERE p.status 1 ORDER BY p.is_top DESC, p.create_time DESC LIMIT #{offset}, #{pageSize}注意这里用了INNER JOIN避免LEFT JOIN带来不必要的NULL数据。分页用LIMIT实现MyBatis里手动计算offset。3.3 初始化数据脚本为了方便调试和演示数据库中一定要有一些预设数据。特别是帖子分类这是几乎每个交流平台都需要的功能。我建议插入这些基础分类二手交易、学习资料、校园生活、社团活动、失物招领、寻物启事、考研考公、求职实习。每个分类下再插入几条示例帖子保证首页一打开就有内容显示。很多同学做完项目管理员登录后台看到一片空白还以为程序出了问题其实只是数据库没初始化数据。4. 分层代码实现与关键业务逻辑4.1 实体类、Mapper接口与XML的对应关系ssm项目最关键的就是三层对应关系JSP页面 - Controller - Service - Mapper - 数据库。实体类、Mapper接口、Mapper XML这三者的字段必须完全对应否则MyBatis会给你报一堆奇怪的错误。以用户实体类为例public class User { private Integer id; private String username; private String password; private String nickname; private String avatar; private String major; private Date createTime; // getter和setter省略 }注意createTime对应数据库的create_time我在applicationContext.xml里开启了mapUnderscoreToCamelCase这样MyBatis可以自动完成下划线到驼峰的映射不需要在XML里写resultMap手动映射。这是一个很省事的小技巧也是很多人不知道的配置项。Mapper接口public interface UserMapper { User selectByUsername(String username); User selectById(Integer id); int insert(User user); int update(User user); }对应的UserMapper.xmlmapper namespacecom.schoolcommunity.mapper.UserMapper select idselectByUsername resultTypeUser SELECT * FROM user WHERE username #{username} /select select idselectById resultTypeUser SELECT * FROM user WHERE id #{id} /select insert idinsert parameterTypeUser useGeneratedKeystrue keyPropertyid INSERT INTO user (username, password, nickname, avatar, major) VALUES (#{username}, #{password}, #{nickname}, #{avatar}, #{major}) /insert /mapper这里最关键的是namespace必须和Mapper接口全限定名一致SQL的id必须和方法名一致parameterType和resultType可以简写为别名User是因为我在SqlSessionFactory里配置了typeAliasesPackage。4.2 用户注册登录的完整流程注册和登录是交流平台的入口功能麻雀虽小五脏俱全它涵盖了表单校验、密码加密、Session管理、拦截器处理等几乎所有ssm的基础知识点。注册流程是这样的JSP页面提交username、password、nickname等参数Controller接收参数先调用Service检查用户名是否已存在不存在则对密码进行MD5加密调用Mapper插入用户记录返回注册成功跳转到登录页Controller代码Controller RequestMapping(/user) public class UserController { Autowired private UserService userService; PostMapping(/register) public String register(User user, Model model) { if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) { model.addAttribute(msg, 用户名和密码不能为空); return register; } boolean success userService.register(user); if (success) { return redirect:/login.jsp; } else { model.addAttribute(msg, 用户名已存在); return register; } } PostMapping(/login) public String login(String username, String password, HttpSession session, Model model) { User user userService.login(username, password); if (user null) { model.addAttribute(msg, 用户名或密码错误); return login; } session.setAttribute(loginUser, user); return redirect:/post/list; } }登录成功后把用户对象放进Session后续页面通过判断Session是否为空来决定显示登录按钮还是用户信息。4.3 帖子发布、分页查询与浏览计数帖子发布本身逻辑不复杂但要注意两个点发布人ID要从Session里取不能信任用户传来的参数发布时间由数据库自动生成不用Java代码去new Date()赋值。这样做能避免用户伪造参数、时间不准的问题。分页查询是交流平台的另一个高频考点。手动实现分页并不难关键是封装一个PageBeanpublic class PageBeanT { private int pageNum; // 当前页码 private int pageSize; // 每页记录数 private long total; // 总记录数 private int totalPages; // 总页数 private ListT list; // 当前页数据 }Service层需要查两次数据库一次查全表count一次查当前页数据然后把数据塞进PageBean返回给ControllerController再放进Model传给JSP页面。JSP页面用c:forEach遍历显示底部用c:if判断上一页下一页。浏览量统计我建议用懒加载思路不每次点击都立刻select update而是页面加载后在JavaScript里发一个异步请求调用 /post/view/{id} 接口后台简单执行一次UPDATE post SET view_count view_count 1。这样做的好处是不影响页面的渲染速度也符合这个规模项目的实际需求。5. 调试部署中的常见问题排障实录5.1 Maven依赖下载慢或jar包缺失国内Maven默认中央仓库下载慢是绝大多数人遇到的第一道坎。解决方式是把Maven的settings.xml里的镜像地址改成阿里云镜像这个操作比去网上找资源手动拷贝jar包靠谱一万倍。配置阿里云镜像的核心内容mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/central/url /mirror改完之后在IDEA里打开Maven面板点击刷新按钮重新加载依赖基本几秒到几十秒就能下完。5.2 启动Tomcat后404或路径不正确这个问题的可能性非常多。常见的原因有项目没有正确部署到Tomcat的webapps目录IDEA里要配置Artifacts访问URL里忘了加项目上下文路径默认是 /项目名_war_exploded/SpringMVC的RequestMapping路径写错。我的排查习惯是分三步走先看IDEA控制台日志找“Deployment is running”和“Artifact is being deployed”字样确认项目已经启动再看Tomcat日志里有没有报Bean创建异常或映射异常最后直接访问静态资源测试比如 /static/css/common.css如果静态资源都404问题出在SpringMVC的静态资源放行配置如果静态资源能访问说明问题在Controller或者视图路径5.3 mybatis报BindingException或Invalid bound statement这个错误很有名报错信息是类似这样的org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.schoolcommunity.mapper.UserMapper.selectByUsername出现这个问题的原因90%是Mapper XML文件没有被打包到target/classes目录或者XML文件路径不对。解决办法就是去target目录看一眼如果classes下没有mapper文件夹说明是Maven资源过滤问题。在pom.xml里加上build resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes /resource resource directorysrc/main/resources/directory includes include**/*.properties/include include**/*.xml/include /includes /resource /resources /build还要检查一下XML文件的namespace是不是写了中文空格或者多了一个字符这种隐蔽问题报错时不会提示只能靠肉眼对比。5.4 数据库连接报Communications link failure这个一般是MySQL服务没有启动或者连接地址的端口写错了。校园里有些电脑MySQL开机不自启命令行输入service mysql start或者net start mysql手动启动。本机连接用localhost:3306不要用127.0.0.1之外的IP。还要注意如果MySQL密码含有特殊字符比如、!等在jdbc.properties里可能被误解析建议密码不要用特殊字符或者用反斜杠转义。6. 项目亮点提炼与个人心法总结6.1 这个项目还能加什么亮点如果你在答辩或求职时想突出一点差异化这几个方向可以考虑帖子热度排行通过like_count和comment_count加权计算一个hotScore字段每天定时任务刷新敏感词过滤服务发帖时自动检测替换违规词汇这个功能校园类平台特别加分用户积分体系签到、发帖、评论都能加积分积分高的用户头衔更高级Redis缓存热门帖子列表缓存穿透和缓存击穿的基础讲解可以写进项目文档6.2 实际做这个项目我的体会我把这个项目完整从零跑通过两三遍最大的感受是ssm框架的难点不在某一个框架本身而在于三个框架协作时的配置细节。很多人学Spring的时候觉得SpringSoC很简单学MyBatis的时候觉得ORM也不难但一到整合各种配置一多就开始手忙脚乱。解决这个问题最好的方式不是背配置而是理解每一个配置背后在解决什么问题数据源给谁用SqlSessionFactory怎么知道mapper文件在哪事务管理器控制的是什么粒度的操作把这三个问题想透彻了ssm项目就算真正入门了。调试部署这件事耐心比技术更重要。我见过太多人报错先百度百度一堆方法挨个试最后还解决不了。我的经验是先看第一行异常找到自己的项目代码在哪个类哪一行后面那一大串Caused by都是第三方框架的内部堆栈对定位问题没有实际帮助。养成看第一行日志、找自己代码的习惯排查效率能高出一倍。如果你准备把这个项目拿去做课程设计或者毕业设计的前置演练我会建议在动手写代码之前先把数据库的表设计文档画清楚把每个页面的跳转关系列出来把Controller的URL规划好再来设计包结构和类。先把这些设计工作做扎实后面的编码进度会快得超出预期。这个项目难度适中从环境搭建到调试部署全流程走一遍你基本就把ssm项目的整条链路吃透了。