1. SSM框架全景解读:为什么它曾是Java开发的黄金标准
十年前我刚入行Java后端开发时,SSM框架组合就像现在的Spring Boot一样无处不在。这套由Spring Framework、Spring MVC和MyBatis组成的"三件套",至今仍是许多传统企业项目的技术底座。让我们先拆解这个经典组合的核心价值:
Spring Framework的IoC容器是整个架构的中枢神经,它通过依赖注入(DI)实现了组件间的松耦合。记得我第一次看到@Autowired注解时的震撼——原来对象依赖可以不用手动new!而AOP切面编程更是把日志、事务这些横切关注点从业务代码中彻底解耦出来。
Spring MVC则提供了优雅的Web层解决方案。它的前端控制器模式(DispatcherServlet)让我摆脱了Servlet API的繁琐,配合@Controller注解,一个简单的CRUD接口从原来的50行代码缩减到10行以内。最精妙的是它的视图解析机制,无论是返回JSON还是JSP页面都能优雅处理。
MyBatis作为持久层框架,在Hibernate的全自动和JDBC的全手动之间找到了完美平衡点。XML映射文件把SQL的可控性和对象映射的便利性结合得恰到好处。特别是动态SQL功能,在处理复杂查询条件时比Hibernate的Criteria API直观得多。
实战经验:在金融行业项目中,MyBatis的SQL优化能力让我们把关键交易的响应时间从200ms压到了80ms以下,这是纯ORM框架很难达到的
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境搭建与基础配置实战
2.1 Maven依赖的黄金组合
创建SSM项目首先要在pom.xml中配置三大框架的依赖。这里有个版本兼容性的坑我踩过多次:
xml复制<!-- Spring核心容器 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- MyBatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MyBatis-Spring整合包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
特别注意mybatis-spring的版本必须同时兼容MyBatis和Spring的版本,否则会出现诡异的NoClassDefFoundError。我曾经因为版本冲突浪费了整整两天时间排查问题。
2.2 配置文件的交响乐
SSM的配置文件就像乐队的乐谱,需要精确协调:
- web.xml - 配置DispatcherServlet和ContextLoaderListener
xml复制<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
- dispatcher-servlet.xml - Spring MVC专属配置
xml复制<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 静态资源处理 -->
<mvc:resources mapping="/static/**" location="/static/"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
- applicationContext.xml - 核心Spring容器配置
xml复制<!-- 数据源配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/ssm_demo"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- MyBatis SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.dao"/>
</bean>
<!-- 事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
避坑指南:数据源配置建议使用Druid而不是基础的BasicDataSource,它不仅提供连接池功能,还能监控SQL性能。我在生产环境通过Druid的监控界面发现并优化了多个慢查询
3. 典型业务场景开发全流程
3.1 用户管理模块实战
假设我们要开发一个用户管理模块,包含基本的CRUD操作。先看领域模型设计:
java复制// User.java
public class User {
private Long id;
private String username;
private String password;
private Integer status;
// getters/setters...
}
// UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
@ResponseBody
public User getById(@PathVariable Long id) {
return userService.getById(id);
}
@PostMapping
@ResponseBody
public Result addUser(@RequestBody User user) {
return userService.addUser(user);
}
}
Service层实现要特别注意事务控制:
java复制@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Transactional
@Override
public Result addUser(User user) {
if(userMapper.existsByUsername(user.getUsername())) {
return Result.fail("用户名已存在");
}
userMapper.insert(user);
return Result.success(user.getId());
}
}
MyBatis映射文件示例:
xml复制<!-- UserMapper.xml -->
<mapper namespace="com.example.dao.UserMapper">
<select id="getById" resultType="User">
SELECT * FROM t_user WHERE id = #{id}
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user(username, password)
VALUES(#{username}, #{password})
</insert>
<select id="existsByUsername" resultType="boolean">
SELECT COUNT(1) FROM t_user WHERE username = #{username}
</select>
</mapper>
3.2 复杂查询与分页处理
实际项目中最常见的需求就是带条件的分页查询。在SSM中实现这个功能需要一些技巧:
首先定义PageHelper分页参数:
java复制@GetMapping
@ResponseBody
public PageInfo<User> listUsers(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String username) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userService.listByCondition(username);
return new PageInfo<>(users);
}
Service层对应的MyBatis动态SQL:
xml复制<select id="listByCondition" resultType="User">
SELECT * FROM t_user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
AND status = 1
</where>
ORDER BY id DESC
</select>
这里有几个关键点:
- PageHelper.startPage()必须紧挨在查询方法前调用
- 分页参数通过ThreadLocal传递,所以要注意线程安全问题
- 复杂条件查询建议使用
标签避免SQL语法错误
4. 性能优化与高级特性
4.1 二级缓存配置实战
MyBatis的二级缓存可以显著提升查询性能,但要谨慎使用:
xml复制<!-- 在mybatis-config.xml中开启全局缓存 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 在Mapper.xml中配置缓存 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
重要注意事项:
- 缓存只适用于单表查询,多表关联查询不要使用
- 更新操作会自动清空缓存,高并发写入场景慎用
- 分布式环境需要集成Redis等中央缓存
4.2 Spring AOP实战案例
利用AOP统一处理日志和异常:
java复制@Aspect
@Component
public class ControllerAspect {
private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);
@Pointcut("execution(* com.example.controller..*.*(..))")
public void controllerPointcut() {}
@Around("controllerPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
logger.info("[Success] {}.{} - {}ms",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(),
System.currentTimeMillis() - start);
return result;
} catch (BusinessException e) {
logger.warn("[Business Error] {}", e.getMessage());
return Result.fail(e.getMessage());
} catch (Exception e) {
logger.error("[System Error]", e);
return Result.error("系统繁忙");
}
}
}
这个切面实现了:
- 方法执行时间监控
- 业务异常统一处理
- 系统异常兜底处理
- 操作日志统一记录
5. 从SSM到Spring Boot的平滑升级
虽然现在Spring Boot已成主流,但理解SSM的核心原理对掌握Spring生态至关重要。实际上,Spring Boot只是对SSM的封装和自动化配置:
- Spring Boot Starter Web = Spring MVC + 内嵌Tomcat
- Spring Boot Starter Data = Spring JDBC + 事务管理
- MyBatis Starter = MyBatis + MyBatis-Spring
迁移建议:
- 先保持现有XML配置,逐步替换为Java Config
- 用@SpringBootApplication替换多个XML配置文件
- 将properties配置迁移到application.yml
- 逐步引入Spring Boot的自动配置特性
我在迁移一个老项目时采用渐进式策略:先用Spring Boot启动原有SSM配置,然后每周迁移2-3个模块到纯Spring Boot风格,三个月后整个项目就完成了现代化改造。
