Mybatis-Spring重要组件介绍

一、四个核心组件

1、SqlSessionTemplate

对应Mybatis的SqlSession,通过SqlSessionFactory类得到。

2、MapperFactoryBean

将Mapper代理对象作为Spring Bean管理,通过getObject()可以得到Mapper代理对象。

3、SqlSessionFactoryBean

通过IOC来创建SqlSessionFactory对

4、MapperScannerConfigurer

自动化注册Mapper接口

二、SqlSessionTemplate

角色:SqlSession在Spring框架下的替代品

核心作用:

替代原生 SqlSession,实现与 Spring 事务管理的无缝集成

线程安全:可在多个 DAO 间共享(原生 SqlSession非线程安全)

自动参与Spring事务(通过 TransactionTemplate)

关键特性:

java 复制代码
public class SqlSessionTemplate implements SqlSession {
    private final SqlSessionFactory sqlSessionFactory;
    private final ExecutorType executorType;
    private final SqlSessionInterceptor interceptor; // 关键:拦截器处理事务
}

执行流程:

java 复制代码
当调用 selectOne()等方法时,通过拦截器检查当前是否存在 Spring 事务
若存在事务,则复用事务内的 SqlSession(通过 TransactionSynchronizationManager绑定)
若无事务,则创建新会话并在操作后关闭(避免资源泄漏)

三、MapperFactoryBean

角色:Mapper接口的Spring Bean工厂,通过MapperFactoryBean的getObject得到的不是MapperFactory,而是MapperProxy,也就是Mapper的代理对象,因为getSqlSession().getMapper(mapperInterface)这个方法,底层就是通过Mybatis的getMapper()方法来得到代理对象,所以返回的内容和原生的是一样的类型MapperProxy。

核心作用:

为每个 Mapper 接口生成 Spring Bean

将MyBatis的Mapper代理对象纳入Spring 容器管理

java 复制代码
public class MapperFactoryBean<T> extends SqlSessionDaoSupport {
    private Class<T> mapperInterface;
    
    @Override
    protected void checkDaoConfig() {
        // 注册 Mapper 接口到Configuration,为什么要添加到Configuration?
        getSqlSession().getConfiguration().addMapper(mapperInterface);
    }
    
    @Override
    public T getObject() {
        // 获取Mapper的代理对象,通过SqlSessionTemplate获取Mapper代理对象。
        return getSqlSession().getMapper(mapperInterface);
    }
}

四、MapperScannerConfigurer

角色:自动扫描并注册Mapper接口

核心作用:

1、自动检测指定包下的Mapper接口

2、为每个接口创建MapperFactoryBean的BeanDefinition

其实说白了:MapperScannerConfigurer是MyBatis-Spring整合包中,为了简化Spring环境下,Mapper接口的批量注册而设计的,是为了批量注册。

xml 复制代码
配置示例:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.mapper"/>
</bean>
效果等价于:
<!-- 自动生成类似配置 -->
<bean id="userMapper" class="MapperFactoryBean">
    <property name="mapperInterface" value="com.example.mapper.UserMapper"/>
</bean>

问题:原生 MyBatis中如何注册Mapper接口?

在原生的MyBatis 中,注册 Mapper 接口主要有两种方式:

xml 复制代码
1、编程式逐个注册:通过 Configuration对象的 addMapper()方法。
2、包扫描注册:通过 Configuration对象的 addMappers()方法,指定一个包名,MyBatis 会扫描该包下的所有接口并注册。
  1. 编程式逐个注册示例:
java 复制代码
SqlSessionFactory sqlSessionFactory = ...; // 获取 SqlSessionFactory
Configuration configuration = sqlSessionFactory.getConfiguration();
configuration.addMapper(UserMapper.class);
configuration.addMapper(OrderMapper.class);
// ... 其他 Mapper 接口
  1. 包扫描注册示例:
java 复制代码
//这行代码会扫描 com.example.mapper包下的所有接口,
//并为它们创建动态代理工厂(MapperProxyFactory)并注册到 Configuration中。
configuration.addMappers("com.example.mapper");

对比MapperScannerConfigurer 在MyBatis-Spring 中,MapperScannerConfigurer的作用是自动扫描指定的包路径,并为每个Mapper接口创建对应的MapperFactoryBean(Spring Bean),从而将Mapper接口注册为Spring容器中的Bean。

同时,在背后也会调用MyBatis的addMapper或类似机制将接口注册到MyBatis的Configuration中。

什么意思:

就是原生的Mapper是通过configuration.addMapper(UserMapper.class)添加进去的。

Mybatis-Spring呢 ?

是通过MapperScannerConfigurer对configuration.addMapper(UserMapper.class)做了一层包装,底层就是使用configuration.addMapper(UserMapper.class)。

问题:为什么要把Mapper接口注册到Configuration中?

1、MyBatis的核心思想是通过接口方法直接映射到 SQL 语句

2、注册过程(addMapper)会解析接口方法上的注解(如 @Select)或对应的 XML 映射文件,将方法签名与 SQL 语句绑定。

五、SqlSessionFactoryBean

角色:Spring 环境下的 SqlSessionFactory工厂

核心作用:

替代原生 SqlSessionFactoryBuilder,通过 Spring IOC 创建 SqlSessionFactory

可以这么理解SqlSessionFactoryBuilder是Mybatis定义的东西,在原生Mybatis中SqlSessionFactoryBuilder是为了得到SqlSessionFactory。

而在Spring中,SqlSessionFactoryBean就是为了得到SqlSessionFactory

支持 Spring 配置方式加载 MyBatis 配置(如 DataSource注入)

六、流程总结

(1) 数据源配置(Spring Boot自动配置)

yaml 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

(2) MyBatis配置

java 复制代码
@Configuration
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        // 设置其他配置,如mapperLocations等
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

(3) Mapper扫描

java 复制代码
@Configuration
@MapperScan("com.example.mapper")
public class MapperScannerConfig {
    // 使用@MapperScan注解自动扫描
}

步骤1: 创建SqlSessionFactory

触发时机: Spring容器初始化SqlSessionFactoryBean时

关键步骤:

java 复制代码
1、SqlSessionFactoryBean实现InitializingBean接口,在afterPropertiesSet()方法中构建SqlSessionFactory
2、解析配置(如mybatis-config.xml,如果指定) 
3、注册类型处理器、插件等
4、解析mapperLocations指定的XML文件(如果有) 
5、最终返回DefaultSqlSessionFactory实例

步骤2: 扫描Mapper接口

触发时机: @MapperScan注解由MapperScannerRegistrar处理

关键步骤:

java 复制代码
1、创建ClassPathMapperScanner扫描指定包
2、过滤出接口(Mapper必须是接口)
3、为每个Mapper接口创建BeanDefinition,并设置BeanClass为MapperFactoryBean
4、设置构造参数为Mapper接口类型
5、设置依赖的SqlSessionFactory或SqlSessionTemplate

步骤3: 初始化MapperFactoryBean

触发时机: Spring容器首次访问Mapper Bean时(懒加载或立即初始化)

关键步骤:

java 复制代码
MapperFactoryBean继承SqlSessionDaoSupport,需要设置SqlSessionFactory或SqlSessionTemplate
在checkDaoConfig()方法中注册Mapper接口到Configuration
在getObject()方法中调用SqlSession.getMapper()

步骤4: 创建Mapper代理对象

触发时机: MapperFactoryBean.getObject()被调用时

关键步骤:

java 复制代码
1、通过SqlSessionTemplate.getMapper()获取代理
2、SqlSessionTemplate内部通过Configuration.getMapper()获取
3、Configuration.getMapper()从MapperRegistry获取MapperProxyFactory
4、MapperProxyFactory创建MapperProxy动态代理

调用流程示例

当调用UserMapper方法时:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper; // 实际是MapperProxy代理对象

    public User getUserById(int id) {
        return userMapper.selectById(id); // 触发代理逻辑
    }
}

代理执行链:

java 复制代码
1、MapperProxy.invoke()
2、创建或获取MapperMethod(封装SqlCommand和MethodSignature)
3、根据SQL类型调用SqlSessionTemplate的方法
例如:sqlSession.selectOne(command.getName(), param)
4、SqlSessionTemplate检查当前线程是否存在事务上下文
5、如果存在,复用已有的SqlSession
6、否则创建新的SqlSession(执行后自动关闭)
7、最终由Executor执行SQL并返回结果
相关推荐
盖世英雄酱581365 小时前
java深度调试【第二章通过堆栈分析性能瓶颈】
java·后端
星月昭铭6 小时前
Spring MVC 接口匹配性能优化:.do后缀引发的性能瓶颈分析
spring·性能优化·tomcat
没有bug.的程序员6 小时前
AOP 原理深剖:动态代理与 CGLIB 字节码增强
java·spring·aop·动态代理·cglib
2401_837088506 小时前
ResponseEntity - Spring框架的“标准回复模板“
java·前端·spring
lang201509286 小时前
Spring Boot RSocket:高性能异步通信实战
java·spring boot·后端
默默coding的程序猿6 小时前
1.北京三维天地公司-实施实习生
java·sql·技术支持·面经·实施·实施工程师·三维天地
天天摸鱼的java工程师7 小时前
解释 Spring 框架中 bean 的生命周期:一个八年 Java 开发的实战视角
java·后端
尤老师FPGA7 小时前
LVDS系列32:Xilinx 7系 ADC LVDS接口参考设计(三)
android·java·ui
自由的疯7 小时前
Java 如何学习 Jenkins?
java·架构