在学习 MyBatis 整合 Spring 的过程中,对一些核心流程进行了整理。通过学习 MyBatis 整合 Spring 的思想,让我们后续在自定义组件与 Spring 整合时有一定的参考价值,并且可以学习到 Spring 框架围绕 Bean 的生命周期提供的各种接口。
Mybatis 整合 Spring 流程
-
Mapper接口交给 Spring IOC管理
- 定义 @MapperScan 注解扫描Mapper接口
- 定义 MapperScannerRegistrar 类 获取 @MapperScan 的属性信息
- 定义 MapperScannerConfigurer 类将 MapperScannerRegistrar 获取到的属性信息传递给该类管理
- 定义 ClassPathMapperScanner 类加载 basePackage 路径下的 Mapper 接口,构建 BeanDefinitions
- 定义 MapperFactoryBean 类实现 Mapper 接口可以被 Spring IOC 管理
-
SqlSesssion 交给 Spring 管理
在使用 Mybatis 中,我们需要通过手动创建 SqlSessionFactory 对象来获取 SqlSession,然后通过SqlSession 获取 mapper 代理对象,进行数据库操作。而在 MyBatis-Spring 中 需要将 SqlSessionFactory 和 Mapper 代理对象交给了 Spring 去创建和管理
- 创建 SqlSessionFactoryBean 加载并解析 Mapper.xml文件 并最终提供 SqlSessionFactory
- 创建 SqlSesssionTemplate 获取 SqlSesssion
-
事务交给 Spring 管理,使用 SpringDataSource
- 创建 SpringManagedTransaction
- 创建 SpringManagedTransactionFactory 获取 SpringManagedTransaction
- 使用 Spring TransactionSynchronizationManager 管理事务,用户不再关注事务的提交与回滚。
Spring基础
- @Import 通过快速导入的方式实现把实例加入spring的IOC容器中
- @Import({TestA.class}):这样就会把 TestA 注入进 IOC 容器,生成一个名字为 “com.demo.testA” 的 bean,同时也可以看到可以传入多个类,这样就可以在IOC容器里生成多个 bean。
- ImportBeanDefinitionRegistrar 接口
- 该接口主要功能是参与BeanFactory的建造,ImportBeanDefinitionRegistrar需要配合@Import注解,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。
- @AliasFor
- 是一个用于给其它注解属性声明别名的注解。将一个注解上的属性值传递给另一个注解或者将同一个注解类的属性设置互为别名.
- BeanFactoryPostProcessor 接口
- spring IoC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据),并可以修改它。同时可以定义多个BeanFactoryPostProcessor,通过设置'order'属性来确定各个BeanFactoryPostProcessor执行顺序。
- BeanDefinitionRegistryPostProcessor 接口
- 可以修改bean定义中属性
- 可以动态的添加bean到spring容器中
- 它只包括postProcessBeanDefinitionRegistry方法,并且继承了 BeanFactoryPostProcessor 接口
- InitializingBean 接口
- InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是实现该接口的类,在初始化bean的时候都会执行该方法。
- ApplicationContextAware 接口
- ApplicationContextAware的作用是可以方便获取Spring容器ApplicationContext,从而可以获取容器内的Bean。它只包括setApplicationContext方法。
- BeanNameAware 接口
- 让Bean获取自己在BeanFactory配置中的名字(根据情况是id或者name)
- FactoryBean 接口
- FactoryBean包装一个对象,使用getObject()方法来获取真正的实例化对象
- FactoryBean包装的对象一般来自第三方,无法添加@Component等注解,就无法直接被Spring管理
- ApplicationListener<?> 接口
- TransactionSynchronizationManager
- TransactionSynchronizationManager 是事务同步管理器。我们可以自定义实现TransactionSynchronization类,来监听 Spring 的事务操作。可以在事务提交之前、之后,回调TransactionSynchronization类的方法。
- 在 MyBatis-Spring 中,会将 SqlSession 和 TransactionSynchronizationManager进行绑定,由 Spring 来管理事务,当事务发生提交/回滚等操作时,会回调 MyBatis-Spring 中的SqlSessionSynchronization 类来处理回调逻辑。
- 其他用途:数据库事务提交成功后进行异步操作、自定义springcache实现事务提交后处理缓存
MyBatis_Spring核心类
MyBatis Mapper接口加载到 Spring IOC 中的流程
在之前使用 MyBatis 中,mapper 接口被扫描到后,会直接为 Mapper 接口生成代理对象执行CRUD 操作。但在 Spring 中 Mapper 接口需要被 Spring IOC 容器管理,所以在 IOC 容器初始化时,需要加载 MyBatis 的 一个个Mapper 接口,并配置成 MapperFactoryBean 类型的 Bean 对象。最终在业务类中使用时就可以直接注入 Mapper 接口进行使用。背后的代理操作被 MapperFactoryBean 这个类所封装。
1.Spring IOC 容器初始化,刷新上下文,注册 BeanDefinition 后,@MapperScan 注解会被扫描到并会加载其配置的 MapperScannerRegistrar 类到 IOC 容器中。MapperScannerRegistrar 会获取到 @MapperScan 配置的包路径,将包路径 basePackages 添加到 MapperScannerConfigurer 的属性中并注册到 BeanDefinitionRegistrar 中。
2.IOC后置处理 BeanDefinition,此时会加载 MapperScannerConfigurer,MapperScannerConfigurer会创建 ClassPathMapperScanner,ClassPathMapperScanner 会根据 basePackages 路径扫描加载 Mapper 接口,将一个个 Mapper 接口注册为 MapperFactoryBean 类型的 BeanDefinition,并添加到BeanDefinitionRegistrar 中。
3.IOC 完成 BeanDefinition 的加载,最终根据注册的 BeanDefinition 实例化 Mapper 接口对象。
SqlSessionFactoryBean 的配置
在之前使用 MyBatis 中,我们需要手动创建 SqlSessionFactory 对象来获取 SqlSession,然后通过SqlSession 获取 Mapper 代理对象,进行数据库操作。
// 根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过SqlSessionFactory获取SqlSession实例
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
而在 Spring 中,SqlSessionFactory 和 Mapper 接口代理对象都将交给 Spring IOC 管理。而 SqlSessionFactory 会通过 SqlSessionFactoryBean 进行创建。
<bean id="sqlSessionFactory" class="com.jokerku.passer.mybatis.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
SqlSessionFactoryBean实现了三个重要的 Spring 接口:
- FactoryBean< SqlSessionFactory>
- InitializingBean
- ApplicationListener
三个 Spring 接口用法意义见上方。
SqlSessionFactoryBean 实际还是调用的 SqlSessionFactoryBuilder 的 bulider 方法,返回了SqlSessionFactory 对象,最终通过调用 Spring 的 FactoryBean#getObject() 获取该对象并注入到IOC中。如果不通过 SqlSessionFactoryBean 来获取 SqlSessionFactory,那么我们需要各种添加配置,代码会变得很臃肿。
而 SqlSessionFactoryBean 为我们设置了很多默认配置,我们只需要注入数据库连接信息和 mapper.xml 的位置,就可以了。
SqlSessionTemplate 与 SqlSessionUtils
SqlSessionTemplate 是 MyBatis-Spring 整合的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替代码中已经在使用的 SqlSession。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。 SqlSessionTemplate 提供 SqlSession 为 SqlSessionInterceptor 代理对象,SqlSessionInterceptor 通过动态代理将 SqlSession 的事务性操作交给了 TransactionSynchronizationManager, 使得 Spring 可以管理事务。 SqlSessionUtils 是用于管理 SqlSession 生命周期的工具类,SqlSessionTemplate 提供的 SqlSession 最终是通过 SqlSessionUtils 获取的。SqlSessionUtils 不仅提供了 SqlSession 的获取与关闭操作。同时也向 TransactionSynchronizationManager 注册了 SqlSessionSynchronization 回调类,用于在事务提交/回滚前后 关闭 SqlSession。
MapperFactoryBean 与 SqlSessionDaoSupport
SqlSessionDaoSupport 是一个抽象的支持类,用来提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法。
SqlSessionDaoSupport 最终被 MapperFactoryBean 继承,MapperFactoryBean 是一个个 Mapper 接口的实例化类型,调用 getObject() 方法可以从 SqlSessionTemplate 中获取 Mapper 接口的实例化对象 并被 Spring IOC 管理,业务方可以直接通过 @Autowired 等方式注入 mapper 接口。
在 ClassPathMapperScanner 加载 Mapper 接口时,会将每个 Mapper 接口封装成 MapperFactoryBean 类型的BeanDefinition (org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions)
SpringManagedTransaction
在 MyBatis-Spring 中创建 SpringManagedTransaction 来管理的事务,使用 Spring 提供的 DataSourceUtils 来获取连接。DataSourceUtils 与 TransactionSynchronizationManager 是事务管理中处理线程上下文信息及连接的重要工具。两者间在 Spring 事务机制中配合使用,DataSourceUtils 用来获取和释放连接,并配合 TransactionSynchronizationManager 使用,在事务提交/回滚后释放连接。 在 MyBatis-Spring 中事务的提交和回滚不需要手动控制,只需要使用编程式事务或者声明式事务来包裹方法即可。Spring 通过 DataSourceTransactionManager 进行事务管理。