欢迎光临
我们一直在努力

SSM 集成 — 从“为什么“到“怎么做“全讲透

SSM 集成 — 从"为什么"到"怎么做"全讲透


第一部分:先搞懂"为什么要集成"

没有 SSM 集成之前,三大框架各自为战:

SpringMVC → 只负责 Web 层(接收请求、返回响应)
MyBatis → 只负责持久层(操作数据库)
Spring → 只负责"管理对象"(IoC 容器)

问题来了:

  • SpringMVC 的 Controller 里要调用 Service,Service 谁来 new?
  • Service 里要调用 Mapper,Mapper 谁来 new?
  • Mapper 需要 SqlSessionFactory,SqlSessionFactory 谁来 new?
  • 数据库连接池(数据源)谁来管理?
  • 事务谁来控制?

答案全是 Spring。 Spring 就像一个"大管家",所有组件都由它创建、由它注入、由它管理。

SSM 集成的本质就一句话:

让 Spring 这个"大管家"接管一切:数据源归它管、Mapper 归它管、Service 归它管、Controller 归它管、事务归它管。SpringMVC 和 MyBatis 只管干自己的活,需要什么组件找 Spring 要就行了。


第二部分:整体架构脑图

浏览器请求
│
▼
web.xml ──────────────────────────────────────────────
│ │
│ 1. ContextLoaderListener(服务器启动时触发) │
│ └─ 加载 applicationContext.xml │
│ └─ 创建 Spring 容器(父容器) │
│ │
│ 2. DispatcherServlet(接收所有请求) │
│ └─ 加载 springmvc-servlet.xml │
│ └─ 创建 SpringMVC 容器(子容器) │
└──────────────────────────────────────────────────│
▼
Spring 容器(大管家/父容器)
┌─────────────────────┐
│ 数据源(Druid) │
│ SqlSessionFactory │
│ Mapper 接口代理 │
│ 事务管理器 │
│ Service 组件 │
└─────────────────────┘
│
▼
SpringMVC 容器(子容器)
┌─────────────────────┐
│ DispatcherServlet │
│ Controller 组件 │
└─────────────────────┘
│
请求路径 /get此处写实体类名By此处写实体类名no/10 │
▼
@RestController
Controller 层
│ @Autowired
▼
@Service
Service 层
│ @Autowired
▼
@Mapper
Mapper 接口 ──── XML 映射文件
│
▼
MySQL 数据库

三个容器各管一摊:

  • Spring 容器(applicationContext.xml)→ 管数据源、MyBatis、事务、Service
  • SpringMVC 容器(springmvc-servlet.xml)→ 管 Controller、视图解析
  • web.xml → 粘合层,把上面两个容器串起来

第三部分:集成步骤逐条拆解(每一步都告诉你"为什么")

步骤 0:创建 Maven 工程 + 引入依赖

不用死记每个依赖,按"角色"分类记:

Spring核心 3 件套:spring-context、spring-webmvc、spring-jdbc
(容器) (MVC) (事务+JdbcTemplate)

AOP 1 个: aspectjweaver(事务切面需要)

JSON 1 个: jackson-databind(Controller 返回 JSON 需要)

MyBatis 2 个: mybatis、mybatis-spring
(原生) (Spring 整合 MyBatis 的桥)

数据库 2 个: druid(连接池)、mysql-connector-j(MySQL 驱动)

Servlet 1 个: jakarta.servlet-api(Spring 6.x 需要 Jakarta,不是 javax)

日志 1 个: logback-classic

测试 2 个: spring-test、junit-jupiter

为什么是这些依赖:

  • spring-context 是 Spring 的核心,IoC 容器就在这
  • spring-webmvc 是 SpringMVC,DispatcherServlet 在这
  • spring-jdbc 自带事务管理器和 JdbcTemplate
  • mybatis-spring 是桥梁,让 Spring 能管理 MyBatis 的 SqlSessionFactory 和 Mapper
  • Druid 是目前最流行的连接池
  • jackson 让 Controller 能把 Java 对象自动转成 JSON 返回

步骤 1:web.xml 配置 ContextLoaderListener

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

为什么需要这步:

  • 普通的 Java 项目:new ClassPathXmlApplicationContext("applicationContext.xml") 手动创建容器
  • Web 项目:不能每次请求都创建容器,必须在服务器启动时一次性创建
  • ContextLoaderListener 实现 ServletContextListener,Tomcat 启动时会自动调用它
  • 它做的事就是:读取 applicationContext.xml,创建 Spring 容器,把容器存到 ServletContext 里

一句话:让 Spring 容器随 Tomcat 启动而创建,随 Tomcat 关闭而销毁。


步骤 2:创建 applicationContext.xml(Spring 容器骨架)

<beans xmlns="…"
xmlns:context="…" <!– 用于扫描注解:@Service、@Autowired 等 —>

xmlns:tx="…" <!– 用于事务注解:@Transactional –>
xmlns:aop="…" <!– 用于 AOP:事务切面需要 –>
…>
</beans>

四个命名空间各管什么:

  • context → 扫描注解、加载 properties 文件
  • tx → @Transactional 注解驱动事务
  • aop → 事务底层就是 AOP,没有 AOP 事务不生效
  • 默认的 beans → 最基本的 <bean> 标签

步骤 3:集成 MyBatis(核心重头戏)

这一步是整个集成最复杂的部分,分成 5 个小步骤,顺序不能乱:

1. 引入 db.properties(数据库连接信息)
↓
2. 配置 Druid 数据源(连接池)
↓
3. 配置 SqlSessionFactory(MyBatis 核心工厂)+ 别名
↓
4. 配置 MapperScannerConfigurer(自动扫描 Mapper 接口)
↓
5. 配置事务管理器 + 开启注解事务

为什么要按这个顺序:后面的依赖前面的。

  • SqlSessionFactory 需要 DataSource(2 是 3 的前提)
  • MapperScanner 需要 SqlSessionFactory(3 是 4 的前提)
  • 事务管理器需要 DataSource(2 是 5 的前提)
逐条解释:

1. db.properties:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/此处写数据库名
jdbc.username=root
jdbc.password=root

把数据库配置抽出来,方便改环境。

2. 数据源 DruidDataSource:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
…
</bean>

Druid 连接池管理数据库连接——连接复用,不用每次都创建/销毁,性能提升巨大。

3. SqlSessionFactoryBean:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="此处写pojo包名"/>
</bean>

  • SqlSessionFactory → MyBatis 的核心,所有 SQL 操作都通过它
  • typeAliasesPackage → 给实体类起别名,映射文件中 resultType="此处写实体类名" 不用写全类名

4. MapperScannerConfigurer(理解 SSM 集成的关键):

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="此处写mapper包名"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

  • 它会扫描 此处写mapper包名 包下所有带 @Mapper 注解的接口
  • 自动为每个接口生成代理对象,注入到 Spring 容器中
  • 之后你在 Service 里 @Autowired private 此处写Mapper接口名 deptMapper 就能直接拿到代理对象
  • 没有这个,MyBatis 的 Mapper 接口就是一堆没用的接口,Spring 不会管它们

5. 事务管理器:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>

  • DataSourceTransactionManager → Spring 提供的事务管理器,基于数据源
  • <tx:annotation-driven> → 开启 @Transactional 注解,Service 层加一个注解就能自动管理事务
同时还要写两个文件:

Mapper 接口(Java):

@Mapper // 告诉 Spring:这是个 Mapper,请 MapperScanner 扫描我
public interface 此处写Mapper接口名 {
public 此处写实体类名 get此处写实体类名ById(Integer 此处写列名);
}

映射文件(XML):

<mapper namespace="此处写接口全类名"> <!– namespace = 接口全类名 –>
<select id="get此处写实体类名ById" parameterType="int" resultType="此处写实体类名">
select * from 此处写表名 where 此处写列名=#{此处写列名}
</select>
</mapper>

注意:

  • namespace = Mapper 接口的全限定类名(必须一致)
  • id = 接口中的方法名(必须一致)
  • resultType = 实体类别名(SqlSessionFactory 中配了别名才能简写)

步骤 4:集成 SpringMVC

4.1 web.xml 中配置 DispatcherServlet

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

为什么还要配一个 DispatcherServlet?之前不是已经有 ContextLoaderListener 了吗?

这是 SSM 面试高频考点——父子容器:

ContextLoaderListener 创建的 → Spring 容器(父容器)
└─ 管:数据源、Mapper、Service、事务("底层组件")

DispatcherServlet 创建的 → SpringMVC 容器(子容器)
└─ 管:Controller、视图解析器("Web 层组件")

子容器可以访问父容器的 Bean(Controller 能 @Autowired Service)
父容器不能访问子容器的 Bean(Service 不能 @Autowired Controller)

为什么要分离:

  • 职责分离:Web 层组件和业务层组件分开管理
  • Controller 是 Web 层的东西,不应该让 Spring 根容器去扫描它
  • 分容器后,单元测试可以只加载父容器,不用启动 Web 环境
4.2 创建 springmvc-servlet.xml

<beans xmlns:mvc="…">
<!– 相当于同时配置了 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter –>
<mvc:annotation-driven/>

<!– 只扫描 Controller,不扫描 Service(Service 归父容器管) –>
<context:component-scan base-package="此处写controller包名"/>
</beans>

  • <mvc:annotation-driven> → 一句话搞定 SpringMVC 的基础设施(Jackson 转换 JSON、参数绑定、校验等)
  • <context:component-scan> → 只扫 Controller 包,千万别扫 Service(Service 在父容器里)
4.3 在 Spring 父容器中添加 Service 扫描

<!– 写在 applicationContext.xml 里,不是 springmvc-servlet.xml –>
<context:component-scan base-package="此处写service包名"/>

关键区分:

  • applicationContext.xml 扫描 service、不扫描 controller
  • springmvc-servlet.xml 扫描 controller、不扫描 service
4.4 写三层代码

请求进来 → @RestController(Controller)
│ @Autowired
▼
@Service(Service)
│ @Autowired
▼
@Mapper(Mapper接口) → XML映射文件 → SQL → 数据库

Controller:

@RestController // = @Controller + @ResponseBody(返回 JSON 不用每个方法加 @ResponseBody)
public class 此处写Controller类名 {
@Autowired
private 此处写Service接口名 deptService; // Controller 依赖 Service,Spring 自动注入

@GetMapping("/get此处写实体类名By此处写实体类名no/{此处写列名}")
public 此处写实体类名 get此处写实体类名By此处写实体类名no(@PathVariable Integer 此处写列名) {
return deptService.query此处写实体类名By此处写实体类名no(此处写列名);
}
}

Service:

@Service // 告诉 Spring:我是一个 Service 组件,请把我管理起来
public class 此处写Service实现类名 implements 此处写Service接口名 {
@Autowired
private 此处写Mapper接口名 deptMapper; // Service 依赖 Mapper,Spring 自动注入代理对象

public 此处写实体类名 query此处写实体类名By此处写实体类名no(Integer 此处写列名) {
return deptMapper.get此处写实体类名ById(此处写列名);
}
}

Mapper(前面步骤 3 已经写好了)


步骤 5:完整项目结构

src/main/java/
├── 此处写controller包名/
│ └── 此处写Controller类名.java @RestController
├── 此处写service包名/
│ ├── 此处写Service接口名.java 接口
│ └── impl/
│ └── 此处写Service实现类名.java @Service
├── 此处写mapper包名/
│ └── 此处写Mapper接口名.java @Mapper
└── 此处写pojo包名/
└── 此处写实体类名.java 实体类

src/main/resources/
├── applicationContext.xml Spring 父容器(数据源+MyBatis+事务+Service扫描)
├── springmvc-servlet.xml SpringMVC 子容器(MVC驱动+Controller扫描)
├── db.properties 数据库配置
└── 此处写mapper包路径/
└── 此处写Mapper接口名.xml 映射文件(必须和接口同包同层级)


第四部分:终极记忆口诀

搭建流程 = “依赖 → 容器 → 数据 → Web → 代码”

第一步:pom.xml 引入依赖
9 组依赖:Spring3 + AOP1 + JSON1 + MyBatis2 + 数据库2 + Servlet1 + 日志1 + 测试2

第二步:web.xml 配置
ContextLoaderListener → 启动时加载 Spring 容器
DispatcherServlet → 拦截所有请求

第三步:applicationContext.xml(Spring 父容器)
db.properties → Druid数据源 → SqlSessionFactory → Mapper扫描 → 事务管理器
↑
扫描 service 包 ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ┘

第四步:springmvc-servlet.xml(SpringMVC 子容器)
mvc:annotation-driven + 扫描 controller 包

第五步:写三层代码
Controller(@RestController) → Service(@Service) → Mapper(@Mapper)
↑ ↑ ↑
在子容器中 在父容器中 代理对象在父容器中

MyBatis 配置链口诀:“引数工扫事”

引 → 引入 db.properties
数 → 配置数据源(Druid)
工 → 配置 SqlSessionFactory(MyBatis 核心工厂)
扫 → 配置 MapperScanner(自动扫描 Mapper 接口生成代理)
事 → 配置事务管理器 + 开启注解事务

最容易搞混的两个点:

  • 父子容器谁扫谁:父扫 Service,子扫 Controller,千万别交叉扫
  • Mapper 接口怎么就变成 Bean 了:MapperScannerConfigurer 自动扫描 @Mapper 接口并生成代理对象,这是 mybatis-spring 这个包的功劳

  • 第五部分:关系索引图

    web.xml(启动入口)
    / \\
    / \\
    ContextLoaderListener DispatcherServlet
    (创建父容器) (创建子容器)
    │ │
    ▼ ▼
    applicationContext.xml springmvc-servlet.xml
    ┌─────────────────────┐ ┌──────────────────────┐
    │ db.properties │ │ mvc:annotation-driven│
    │ DruidDataSource │ │ 扫描 Controller │
    │ SqlSessionFactory │ └──────────────────────┘
    │ MapperScannerConfig │ │
    │ 事务管理器 │ │
    │ 扫描 Service │ │
    └─────────────────────┘ │
    │ │
    │ ┌────────────────────────┘
    │ │ 子容器可以访问父容器的 Bean
    │ ▼
    │ Controller ──@Autowired──▶ Service ──@Autowired──▶ Mapper(代理)
    │ │
    └────────────────────────────────────────────────────────┘
    父容器持有所有底层组件


    第六部分:一张表总结所有注解和配置

    角色注解/配置写在哪个文件作用
    实体类 无注解 pojo 包 对应数据库表
    Mapper 接口 @Mapper mapper 包 标记给 MapperScanner 扫描
    映射文件 <mapper namespace="…"> resources 下 SQL 语句,namespace=接口全类名
    Service @Service service 包 标记为 Service 组件,被父容器扫描
    Controller @RestController controller 包 标记为 Controller,被子容器扫描
    依赖注入 @Autowired 各层 Spring 自动注入依赖的 Bean
    数据源 <bean id="dataSource"> applicationContext.xml Druid 连接池
    SqlSessionFactory <bean id="sqlSessionFactory"> applicationContext.xml MyBatis 核心工厂
    Mapper 扫描 MapperScannerConfigurer applicationContext.xml 扫描 @Mapper 接口生成代理
    事务管理器 DataSourceTransactionManager applicationContext.xml 管理数据库事务
    MVC 驱动 <mvc:annotation-driven/> springmvc-servlet.xml 启用 SpringMVC 基础设施
    Service 扫描 <context:component-scan> applicationContext.xml 扫描 service 包
    Controller 扫描 <context:component-scan> springmvc-servlet.xml 扫描 controller 包

    一句话总结:整个 SSM 集成就一条主线——让 Spring 当管家,把数据层(MyBatis)和 Web 层(SpringMVC)需要的所有组件都管起来,然后通过依赖注入把它们串到一起。 记住这个"管家模式",你永远不会忘掉为什么要这样配。

    赞(0)
    未经允许不得转载:171主机测评 » SSM 集成 — 从“为什么“到“怎么做“全讲透
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址