SSM框架整合实战:Spring+SpringMVC+MyBatis企业级开发指南
2026/7/22 12:32:19 网站建设 项目流程

1. SSM框架整合概述

SSM(Spring + Spring MVC + MyBatis)是目前Java企业级开发中最主流的框架组合之一。作为一名有多年Java开发经验的工程师,我见证了这个技术栈从兴起到成为行业标准的过程。相比早期的SSH(Struts2 + Spring + Hibernate)组合,SSM具有更轻量、更灵活、更适合现代Web开发的特点。

在实际项目开发中,我发现很多新手开发者虽然能单独使用这三个框架,但在整合时总会遇到各种问题。本文将分享我在多个生产项目中总结出的SSM整合方案,这个方案已经经过多个百万级用户项目的验证,具有极高的稳定性和可维护性。

2. 环境准备与项目搭建

2.1 开发环境要求

  • JDK 1.8+
  • Maven 3.6+
  • Tomcat 8.5+
  • MySQL 5.7+
  • IntelliJ IDEA/Eclipse

提示:强烈建议使用与教程一致的版本,不同版本间可能存在兼容性问题。

2.2 Maven项目初始化

使用以下命令创建Maven项目骨架:

mvn archetype:generate -DgroupId=com.example -DartifactId=ssm-demo -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

2.3 目录结构调整

标准的SSM项目目录结构如下:

src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ ├── controller/ │ │ ├── service/ │ │ │ ├── impl/ │ │ ├── dao/ │ │ ├── entity/ │ │ └── config/ │ ├── resources/ │ │ ├── spring/ │ │ ├── mybatis/ │ │ └── db.properties │ └── webapp/ │ ├── WEB-INF/ │ │ └── views/ │ └── resources/ └── test/ └── java/

3. 核心框架配置

3.1 Spring配置

3.1.1 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 扫描Service和DAO组件 --> <context:component-scan base-package="com.example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 加载数据库配置文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
3.1.2 db.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=123456

3.2 Spring MVC配置

3.2.1 spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 扫描Controller组件 --> <context:component-scan base-package="com.example.controller"/> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 开启MVC注解驱动 --> <mvc:annotation-driven/> <!-- 静态资源处理 --> <mvc:resources mapping="/static/**" location="/static/"/> </beans>
3.2.2 web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置Spring上下文监听器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置DispatcherServlet --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 字符编码过滤器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>

3.3 MyBatis配置

3.3.1 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 开启驼峰命名自动映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 打印SQL语句 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <typeAliases> <package name="com.example.entity"/> </typeAliases> </configuration>
3.3.2 Spring整合MyBatis配置

在applicationContext.xml中添加:

<!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/> <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/> </bean> <!-- 配置Mapper扫描器 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.example.dao"/> </bean>

4. 业务代码实现

4.1 实体类示例

package com.example.entity; import java.io.Serializable; public class User implements Serializable { private Integer id; private String username; private String password; private String email; // getters and setters // toString() }

4.2 DAO层实现

4.2.1 接口定义
package com.example.dao; import com.example.entity.User; import java.util.List; public interface UserDao { User selectById(Integer id); List<User> selectAll(); int insert(User user); int update(User user); int delete(Integer id); }
4.2.2 Mapper XML
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.dao.UserDao"> <resultMap id="userMap" type="User"> <id property="id" column="id"/> <result property="username" column="username"/> <result property="password" column="password"/> <result property="email" column="email"/> </resultMap> <select id="selectById" resultMap="userMap"> SELECT * FROM user WHERE id = #{id} </select> <select id="selectAll" resultMap="userMap"> SELECT * FROM user </select> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user(username, password, email) VALUES(#{username}, #{password}, #{email}) </insert> <update id="update"> UPDATE user SET username=#{username}, password=#{password}, email=#{email} WHERE id=#{id} </update> <delete id="delete"> DELETE FROM user WHERE id=#{id} </delete> </mapper>

4.3 Service层实现

4.3.1 服务接口
package com.example.service; import com.example.entity.User; import java.util.List; public interface UserService { User getUserById(Integer id); List<User> getAllUsers(); boolean addUser(User user); boolean updateUser(User user); boolean deleteUser(Integer id); }
4.3.2 服务实现
package com.example.service.impl; import com.example.dao.UserDao; import com.example.entity.User; import com.example.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service @Transactional public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User getUserById(Integer id) { return userDao.selectById(id); } @Override public List<User> getAllUsers() { return userDao.selectAll(); } @Override public boolean addUser(User user) { return userDao.insert(user) > 0; } @Override public boolean updateUser(User user) { return userDao.update(user) > 0; } @Override public boolean deleteUser(Integer id) { return userDao.delete(id) > 0; } }

4.4 Controller层实现

package com.example.controller; import com.example.entity.User; import com.example.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; @Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public String getUser(@PathVariable Integer id, Model model) { User user = userService.getUserById(id); model.addAttribute("user", user); return "user/detail"; } @GetMapping("/list") public String listUsers(Model model) { List<User> users = userService.getAllUsers(); model.addAttribute("users", users); return "user/list"; } @GetMapping("/add") public String toAddPage() { return "user/add"; } @PostMapping("/add") public String addUser(User user) { userService.addUser(user); return "redirect:/user/list"; } @GetMapping("/edit/{id}") public String toEditPage(@PathVariable Integer id, Model model) { User user = userService.getUserById(id); model.addAttribute("user", user); return "user/edit"; } @PutMapping("/edit") public String editUser(User user) { userService.updateUser(user); return "redirect:/user/list"; } @DeleteMapping("/{id}") public String deleteUser(@PathVariable Integer id) { userService.deleteUser(id); return "redirect:/user/list"; } }

5. 视图层实现

5.1 用户列表页面 (list.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>用户列表</title> </head> <body> <h2>用户列表</h2> <a href="/user/add">新增用户</a> <table border="1"> <tr> <th>ID</th> <th>用户名</th> <th>邮箱</th> <th>操作</th> </tr> <c:forEach items="${users}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.email}</td> <td> <a href="/user/${user.id}">查看</a> <a href="/user/edit/${user.id}">编辑</a> <a href="/user/${user.id}" onclick="return confirm('确定删除吗?')">删除</a> </td> </tr> </c:forEach> </table> </body> </html>

5.2 用户详情页面 (detail.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>用户详情</title> </head> <body> <h2>用户详情</h2> <p>ID: ${user.id}</p> <p>用户名: ${user.username}</p> <p>邮箱: ${user.email}</p> <a href="/user/list">返回列表</a> </body> </html>

6. 测试与验证

6.1 单元测试

package com.example.service; import com.example.entity.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring/applicationContext.xml") public class UserServiceTest { @Autowired private UserService userService; @Test public void testCRUD() { // 测试新增 User user = new User(); user.setUsername("testUser"); user.setPassword("123456"); user.setEmail("test@example.com"); userService.addUser(user); // 测试查询 User dbUser = userService.getUserById(user.getId()); System.out.println(dbUser); // 测试更新 dbUser.setEmail("new@example.com"); userService.updateUser(dbUser); // 测试列表查询 List<User> users = userService.getAllUsers(); users.forEach(System.out::println); // 测试删除 userService.deleteUser(user.getId()); } }

6.2 集成测试

  1. 启动Tomcat服务器
  2. 访问http://localhost:8080/user/list
  3. 测试各个功能点:
    • 新增用户
    • 查看用户详情
    • 编辑用户信息
    • 删除用户

7. 常见问题与解决方案

7.1 事务不生效问题

问题现象:Service层方法执行异常时,数据库操作没有回滚。

解决方案

  1. 确保在applicationContext.xml中配置了事务管理器
  2. 确保开启了注解驱动<tx:annotation-driven/>
  3. 在Service实现类上添加@Transactional注解
  4. 检查异常类型,默认只对RuntimeException回滚,可通过@Transactional(rollbackFor=Exception.class)修改

7.2 MyBatis映射文件找不到

问题现象:启动时报错,提示找不到Mapper.xml文件。

解决方案

  1. 检查sqlSessionFactorymapperLocations配置路径是否正确
  2. 确保Mapper.xml文件放在了resources/mybatis/mapper目录下
  3. 检查Mapper.xml的文件名是否与接口名一致
  4. Maven项目中,确保resources目录被正确识别(可在pom.xml中添加build配置)

7.3 静态资源访问不到

问题现象:CSS/JS/图片等静态资源无法加载。

解决方案

  1. 在spring-mvc.xml中配置<mvc:resources>
  2. 确保静态资源放在了webapp/static目录下
  3. 检查路径引用是否正确,如<link href="/static/css/style.css" rel="stylesheet">

7.4 中文乱码问题

问题现象:页面显示或数据库存储出现乱码。

解决方案

  1. 确保web.xml中配置了CharacterEncodingFilter
  2. 检查数据库连接URL是否包含useUnicode=true&characterEncoding=UTF-8
  3. 确保JSP页面设置了<%@ page contentType="text/html;charset=UTF-8" %>
  4. 检查Tomcat的server.xml中Connector配置了URIEncoding="UTF-8"

8. 性能优化建议

8.1 数据库连接池优化

推荐使用Druid连接池,配置示例:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <property name="initialSize" value="5"/> <property name="minIdle" value="5"/> <property name="maxActive" value="20"/> <property name="maxWait" value="60000"/> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="validationQuery" value="SELECT 1"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <property name="filters" value="stat,wall"/> </bean>

8.2 MyBatis二级缓存

启用MyBatis二级缓存可以显著提高查询性能:

  1. 在mybatis-config.xml中配置:
<settings> <setting name="cacheEnabled" value="true"/> </settings>
  1. 在Mapper.xml中添加缓存配置:
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>

8.3 SQL性能优化

  1. 为常用查询字段添加索引
  2. 避免使用SELECT *,只查询需要的字段
  3. 复杂查询考虑使用MyBatis的关联查询或多次简单查询
  4. 大数据量查询考虑使用分页插件

9. 项目扩展方向

9.1 集成Redis缓存

  1. 添加Redis依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
  1. 配置RedisTemplate:
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } }

9.2 添加Swagger接口文档

  1. 添加Swagger依赖:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
  1. 配置Swagger:
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.example.controller")) .paths(PathSelectors.any()) .build() .apiInfo(apiInfo()); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("SSM项目API文档") .description("SSM框架整合示例项目") .version("1.0") .build(); } }

9.3 日志系统集成

推荐使用SLF4J + Logback组合:

  1. 添加依赖:
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
  1. 配置logback.xml:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <property name="LOG_HOME" value="/logs"/> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/ssm-demo.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/ssm-demo.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> <logger name="com.example" level="DEBUG"/> </configuration>

10. 生产环境部署建议

10.1 安全配置

  1. 数据库密码加密:使用Jasypt等工具加密配置文件中的敏感信息
  2. 防止SQL注入:使用MyBatis的#{}参数绑定
  3. XSS防护:在Controller层对用户输入进行过滤
  4. CSRF防护:Spring Security提供了CSRF防护支持

10.2 性能调优

  1. Tomcat优化:

    • 调整连接器配置
    • 启用NIO模式
    • 合理设置线程池参数
  2. JVM优化:

    • 设置合适的堆内存大小
    • 选择合适的垃圾收集器
    • 配置GC日志监控

10.3 监控方案

  1. 使用Spring Boot Actuator监控应用健康状态
  2. 集成Prometheus + Grafana实现可视化监控
  3. 使用ELK收集和分析日志
  4. 配置告警机制,对异常情况进行及时通知

11. 总结与经验分享

在实际项目开发中,SSM框架的整合只是基础工作,更重要的是理解每个框架的设计理念和最佳实践。以下是我在多个项目中总结的一些经验:

  1. 分层清晰:严格遵循Controller-Service-Dao的分层架构,每层只关注自己的职责
  2. 事务控制:事务应放在Service层,且粒度要合理,避免大事务
  3. 异常处理:使用统一的异常处理机制,避免直接向上抛出原生异常
  4. 代码规范:遵循团队约定的代码规范,保持风格一致
  5. 测试驱动:为关键业务逻辑编写单元测试和集成测试

对于初学者,建议从简单的CRUD功能开始,逐步深入理解框架的各个组件。遇到问题时,多查阅官方文档和源码,这是最好的学习资料。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询