MyBatis 源码分析 - MyBatis-Spring 源码分析
参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读
MyBatis 版本:3.5.2
MyBatis-Spring 版本:2.0.3
MyBatis-Spring-Boot-Starter 版本:2.1.4
该系列其他文档请查看:《精尽 MyBatis 源码分析 - 文章导读》
在前面的一系列文档中对整个 MyBatis 框架进行了分析,相信你对 MyBatis 有了一个更加深入的了解。在使用它的过程中,需要自己创建 SqlSessionFactory 和 SqlSession,然后获取到 Mapper 接口的动态代理对象,执行数据库相关操作,对这些对象的管理并不是那么简单。我们通常会结合 Spring 来使用 MyBatis,将这些对象作为 Spring Bean 注入到 Spring 容器,也允许参与到 Spring 的事务管理之中
Spring 官方并没有提供对 MyBatis3 的集成方案,于是在 MyBatis 社区将对 Spring 的集成作为一个 MyBatis 子项目 MyBatis-Spring,帮助你将 MyBatis 代码无缝地整合到 Spring 中,那么我们一起来看看这个子项目是如何集成到 Spring 中的
在开始读这篇文档之前,需要对 Spring 有一定的了解,可以结合我的源码注释(Mybatis-Spring 源码分析 GitHub 地址)进行阅读,MyBatis-Spring官方文档
简述
主要涉及到的几个类:
-
org.mybatis.spring.SqlSessionFactoryBean
:实现 FactoryBean、InitializingBean、ApplicationListener 接口,负责构建一个 SqlSessionFactory 对象 -
org.mybatis.spring.mapper.MapperFactoryBean
:实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象 -
org.mybatis.spring.support.SqlSessionDaoSupport
抽象类,继承了 DaoSupport 抽象类,用于构建一个 SqlSessionTemplate 对象 -
org.mybatis.spring.mapper.MapperScannerConfigurer
:实现了BeanDefinitionRegistryPostProcessor、InitializingBean接口,ApplicationContextAware、BeanNameAware接口,用于扫描Mapper接口,借助ClassPathMapperScanner
扫描器对Mapper接口的BeanDefinition对象(Spring Bean 的前身)进行修改 -
org.mybatis.spring.mapper.ClassPathMapperScanner
:继承了ClassPathBeanDefinitionScanner抽象类,负责执行扫描,修改扫描到的 Mapper 接口的 BeanDefinition 对象(Spring Bean的前身),将其 Bean Class 修改为 MapperFactoryBean,从而在 Spring 初始化该 Bean 的时候,会初始化成MapperFactoryBean
类型,实现创建 Mapper 动态代理对象 -
org.mybatis.spring.annotation.MapperScannerRegistrar
:实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口作为
@MapperScann
注解的注册器,根据注解信息注册一个MapperScannerConfigurer
对象,用于扫描 Mapper 接口 -
org.mybatis.spring.config.MapperScannerBeanDefinitionParser
:实现 BeanDefinitionParser 接口,<mybatis:scan />
的解析器,和MapperScannerRegistrar
的实现逻辑一样 -
org.mybatis.spring.SqlSessionTemplate
:实现 SqlSession 和 DisposableBean 接口,SqlSession 操作模板实现类,承担 SqlSessionFactory 和 SqlSession 的职责 -
org.mybatis.spring.SqlSessionUtils
:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对像
大致逻辑如下:
- 通过配置
MapperScannerConfigurer
的 Spring Bean,它会结合ClassPathMapperScanner
扫描器,对指定包路径下的 Mapper 接口对应 BeanDefinition 对象(Spring Bean 的前身)进行修改,将其 Bean Class 修改为MapperFactoryBean
类型,从而在 Spring 初始化该 Bean 的时候,会初始化成MapperFactoryBean
对象,实现创建 Mapper 动态代理对象 - 在
MapperFactoryBean
对象中getObject()
中,根据SqlSessionTemplate
对象为该 Mapper 接口创建一个动态代理对象,也就是说在我们注入该 Mapper 接口时,实际注入的是 Mapper 接口对应的动态代理对象 SqlSessionTemplate
对象中,承担 SqlSessionFactory 和 SqlSession 的职责,结合 Spring 的事务体系进行处理
配置示例
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${url}" />
<property name="driverClassName" value="${driver}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="mySqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- bean的名称为sqlSessionFactory会出现错误 -->
<property name="dataSource" ref="dataSource" />
<!-- 引入配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/fullmoon/study/mapping/*.xml" />
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.fullmoon.study.dao" />
<property name="sqlSessionFactoryBeanName" value="mySqlSessionFactory" />
</bean>
- 上面会创建
DruidDataSource
数据源,SqlSessionFactoryBean
和MapperScannerConfigurer
对象
SqlSessionFactoryBean
org.mybatis.spring.SqlSessionFactoryBean
:实现 FactoryBean、InitializingBean、ApplicationListener 接口,负责构建一个 SqlSessionFactory 对象
关于Spring的FactoryBean
机制,不熟悉的先去了解一下,大致就是Spring在注入该类型的Bean时,调用的是它的getObject()
方法
构造方法
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
private static final MetadataReaderFactory METADATA_READER_FACTORY = new CachingMetadataReaderFactory();
/**
* 指定的 mybatis-config.xml 路径的资源
*/
private Resource configLocation;
private Configuration configuration;
/**
* 指定 XML 映射文件路径的资源数组
*/
private Resource[] mapperLocations;
/**
* 数据源
*/
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
/**
* SqlSession 工厂,默认为 DefaultSqlSessionFactory
*/
private SqlSessionFactory sqlSessionFactory;
// EnvironmentAware requires spring 3.1
private String environment = SqlSessionFactoryBean.class.getSimpleName();
private boolean failFast;
private Interceptor[] plugins;
private TypeHandler<?>[] typeHandlers;
private String typeHandlersPackage;
@SuppressWarnings("rawtypes")
private Class<? extends TypeHandler> defaultEnumTypeHandler;
private Class<?>[] typeAliases;
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
private LanguageDriver[] scriptingLanguageDrivers;
private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
// issue #19. No default provider.
private DatabaseIdProvider databaseIdProvider;
private Class<? extends VFS> vfs;
private Cache cache;
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory;
}
可以看到上面定义的各种属性,这里就不一一解释了,根据名称可以知道属性的作用
afterPropertiesSet方法
afterPropertiesSet()
方法,实现的 InitializingBean 接口,在 Spring 容器中,初始化该 Bean 时,会调用该方法,方法如下:
@Override
public void afterPropertiesSet() throws Exception {
// 校验 dataSource 数据源不能为空
notNull(dataSource, "Property 'dataSource' is required");
// 校验 sqlSessionFactoryBuilder 构建器不能为空,上面默认 new 一个对象
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
// configuration 和 configLocation 有且只有一个不为空
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
// 初始化 SqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
- 校验 dataSource 数据源不能为空,所以配置该Bean时,必须配置一个数据源
- 校验 sqlSessionFactoryBuilder 构建器不能为空,上面默认 new 一个对象
- configuration 和 configLocation 有且只有一个不为空
- 调用
buildSqlSessionFactory()
方法,初始化SqlSessionFactory
buildSqlSessionFactory方法
buildSqlSessionFactory()
方法,根据配置信息构建一个SqlSessionFactory
实例,方法如下:
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
// 初始化 Configuration 全局配置对象
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
// 如果已存在 Configuration 对象
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
// 否则,如果配置了 mybatis-config.xml 配置文件
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
// 否则,创建一个 Configuration 对象
targetConfiguration = new Configuration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
/*
* 如果配置了 ObjectFactory(实例工厂)、ObjectWrapperFactory(ObjectWrapper工厂)、VFS(虚拟文件系统)
* 则分别往 Configuration 全局配置对象设置
*/
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
/*
* 如果配置了需要设置别名的包路径,则扫描该包路径下的 Class 对象
* 往 Configuration 全局配置对象的 TypeAliasRegistry 别名注册表进行注册
*/
if (hasLength(this.typeAliasesPackage)) {
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
// 过滤掉匿名类
.filter(clazz -> !clazz.isAnonymousClass())
// 过滤掉接口
.filter(clazz -> !clazz.isInterface())
// 过滤掉内部类
.filter(clazz -> !clazz.isMemberClass())
.forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
}
/*
* 如果单独配置了需要设置别名的 Class 对象
* 则将其往 Configuration 全局配置对象的 TypeAliasRegistry 别名注册表注册
*/
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
// 往 Configuration 全局配置对象添加 Interceptor 插件
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
// 扫描包路径,往 Configuration 全局配置对象添加 TypeHandler 类型处理器
if (hasLength(this.typeHandlersPackage)) {
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
}
// 往 Configuration 全局配置对象添加 TypeHandler 类型处理器
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
// 设置默认的枚举类型处理器
targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
// 往 Configuration 全局配置对象添加 LanguageDriver 语言驱动
if (!isEmpty(this.scriptingLanguageDrivers)) {
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
targetConfiguration.getLanguageRegistry().register(languageDriver);
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
});
}
// 设置默认的 LanguageDriver 语言驱动
Optional.ofNullable(this.defaultScriptingLanguageDriver)
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
// 设置当前数据源的数据库 id
if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
try {
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
// 添加 Cache 缓存
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
if (xmlConfigBuilder != null) {
try {
// 如果配置了 mybatis-config.xml 配置文件,则初始化 MyBatis
xmlConfigBuilder.parse();
LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
// 设置 Environment 环境信息
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
// 如果配置了 XML 映射文件的路径
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
/*
* 遍历所有的 XML 映射文件
*/
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
// 解析 XML 映射文件
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
}
// 通过构建器创建一个 DefaultSqlSessionFactory 对象
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
方法有点长,主要是通过配置信息创建一个 Configuration 对象,然后构建一个 DefaultSqlSessionFactory 对象
-
初始化
Configuration
全局配置对象- 如果已存在 Configuration 对象,则直接使用该对象
- 否则,如果配置了 mybatis-config.xml 配置文件,则创建一个 XMLConfigBuilder 对象,待解析
- 否则,创建一个 Configuration 对象
-
往 Configuration 对象中设置相关配置属性
-
如果是
1.2
步生成的 Configuration 对象,那么调用XMLConfigBuilder
的parse()
方法进行解析,初始化 MyBatis,在《MyBatis 初始化(一)之加载mybatis-config.xml》中分析过 -
如果配置了 XML 映射文件的路径
mapperLocations
,则进行遍历依次解析,通过创建XMLMapperBuilder
对象,调用其parse()
方法进行解析,在《MyBatis 初始化(二)之加载Mapper接口与映射文件》的XMLMapperBuilder小节中分析过 -
通过
SqlSessionFactoryBuilder
构建器创建一个DefaultSqlSessionFactory
对象
在 MyBatis-Spring 项目中,除了通过 mybatis-config.xml 配置文件配置 Mapper 接口的方式以外,还提供了几种配置方法,这里的
SqlSessionFactoryBean.mapperLocations
也算一种这样在 Spring 中,Mapper 接口和对应的 XML 映射文件名称可以不一致,文件中里面配置
namepace
正确就可以了
getObject方法
getObject()
方法,在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 DefaultSqlSessionFactory 对象,方法如下:
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
// 如果为空则初始化 sqlSessionFactory
afterPropertiesSet();
}
// 返回 DefaultSqlSessionFactory 对象
return this.sqlSessionFactory;
}
onApplicationEvent方法
onApplicationEvent(ApplicationEvent event)
方法,监听 ContextRefreshedEvent 事件,如果还存在未初始化完成的 MapperStatement 们,则再进行解析,方法如下:
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 如果配置了需要快速失败,并且监听到了 Spring 容器初始化完成事件
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
// 将 MyBatis 中还未完全解析的对象,在这里再进行解析
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
MapperFactoryBean
org.mybatis.spring.mapper.MapperFactoryBean
:实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象
构造方法
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
/**
* Mapper 接口
*/
private Class<T> mapperInterface;
/**
* 是否添加到 {@link Configuration} 中,默认为 true
*/
private boolean addToConfig = true;
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
-
mapperInterface
:对应的Mapper接口 -
addToConfig
:是否添加到 Configuration 中,默认为 true
getObject方法
getObject()
方法,在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 Mapper 接口对应的动态代理对象,方法如下:
@Override
public T getObject() throws Exception {
// getSqlSession() 方法返回 SqlSessionTemplate 对象
return getSqlSession().getMapper(this.mapperInterface);
}
getSqlSession()
方法在SqlSessionDaoSupport
中定义,返回的是SqlSessionTemplate
对象,后续会讲到
可以先暂时理解为就是返回一个 DefaultSqlSession,获取mapperInterface
Mapper接口对应的动态代理对象
这也就是为什么在Spring中注入Mapper接口Bean时,我们可以直接调用它的方法
checkDaoConfig方法
checkDaoConfig()
方法,校验该 Mapper 接口是否被初始化并添加到 Configuration 中
@Override
protected void checkDaoConfig() {
// 校验 sqlSessionTemplate 非空
super.checkDaoConfig();
// 校验 mapperInterface 非空
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
/*
* 如果该 Mapper 接口没有被解析至 Configuration,则对其进行解析
*/
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 将该 Mapper 接口添加至 Configuration,会对该接口进行一系列的解析
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
- 校验
sqlSessionTemplate
非空 - 校验
mapperInterface
非空 - 如果该 Mapper 接口没有被解析至 Configuration,则对其进行解析
因为继承了DaoSupport
抽象类,实现了 InitializingBean 接口,在 afterPropertiesSet() 方法中会调用checkDaoConfig()
方法
SqlSessionDaoSupport
org.mybatis.spring.support.SqlSessionDaoSupport
抽象类,继承了 DaoSupport 抽象类,用于构建一个 SqlSessionTemplate 对象,代码如下:
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
@SuppressWarnings("WeakerAccess")
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
public final SqlSessionFactory getSqlSessionFactory() {
return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null);
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
public SqlSessionTemplate getSqlSessionTemplate() {
return this.sqlSessionTemplate;
}
@Override
protected void checkDaoConfig() {
notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
}
setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
方法,将 SqlSessionFactory 构建成我们需要的 SqlSessionTemplate 对象,该对象在后续讲到checkDaoConfig()
方法,校验 SqlSessionTemplate 非空
MapperScannerConfigurer
org.mybatis.spring.mapper.MapperScannerConfigurer
:实现了BeanDefinitionRegistryPostProcessor
、InitializingBean接口,ApplicationContextAware、BeanNameAware接口
用于扫描Mapper接口,借助ClassPathMapperScanner
修改Mapper接口的BeanDefinition对象(Spring Bean 的前身),将Bean的Class对象修改为MapperFactoryBean
类型
那么在Spring初始化该Bean的时候,会初始化成MapperFactoryBean类型
构造方法
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
/**
* Mapper 接口的包路径
*/
private String basePackage;
/**
* 是否要将接口添加到 Configuration 全局配置对象中
*/
private boolean addToConfig = true;
private String lazyInitialization;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
private Class<? extends Annotation> annotationClass;
private Class<?> markerInterface;
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;
private ApplicationContext applicationContext;
private String beanName;
private boolean processPropertyPlaceHolders;
private BeanNameGenerator nameGenerator;
private String defaultScope;
@Override
public void afterPropertiesSet() throws Exception {
notNull(this.basePackage, "Property 'basePackage' is required");
}
}
在SqlSessionFactoryBean小节的示例中可以看到,定义了basePackage
和sqlSessionFactoryBeanName
两个属性
basePackage
:Mapper 接口的包路径addToConfig
:是否要将接口添加到 Configuration 全局配置对象中sqlSessionFactoryBeanName
:SqlSessionFactory的Bean Name
在afterPropertiesSet()
方法中会校验basePackage
非空
在 MyBatis-Spring 项目中,除了通过 mybatis-config.xml 配置文件配置 Mapper 接口的方式以外,还提供了几种配置方法,这里的配置
basePackage
属性也算一种这样在 Spring 中,Mapper 接口和对应的 XML 映射文件名称可以不一致,文件中里面配置
namepace
正确就可以了
postProcessBeanDefinitionRegistry方法
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
方法,在 BeanDefinitionRegistry 完成后进行一些处理
这里会借助ClassPathMapperScanner
扫描器,扫描指定包路径下的 Mapper 接口,方法如下:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
// 处理属性中的占位符
processPropertyPlaceHolders();
}
// 创建一个 Bean 扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 是否要将 Mapper 接口添加到 Configuration 全局配置对象中
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
// 设置 SqlSessionFactory 的 BeanName
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
// 添加几个过滤器
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
- 如果需要处理属性中的占位符,则调用
processPropertyPlaceHolders()
方法 - 创建一个 Bean 扫描器
ClassPathMapperScanner
对象 - 设置一些 Mapper 接口扫描器的属性,例如
addToConfig
、sqlSessionFactoryBeanName
- 调用扫描器的
registerFilters()
方法,添加几个过滤器,过滤指定路径下的 Mapper 接口 - 调用其
scan
方法,开始扫描basePackage
路径下的 Mapper 接口
ClassPathMapperScanner
org.mybatis.spring.mapper.ClassPathMapperScanner
:继承了ClassPathBeanDefinitionScanner抽象类
负责执行扫描,修改扫描到的 Mapper 接口的 BeanDefinition 对象(Spring Bean的前身),将其 Bean Class 修改为 MapperFactoryBean,从而在 Spring 初始化该 Bean 的时候,会初始化成 MapperFactoryBean
类型,实现创建 Mapper 动态代理对象
构造方法
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathMapperScanner.class);
/**
* 是否要将 Mapper 接口添加到 Configuration 全局配置对象中
*/
private boolean addToConfig = true;
private boolean lazyInitialization;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionTemplateBeanName;
/**
* SqlSessionFactory Bean 的名称
*/
private String sqlSessionFactoryBeanName;
private Class<? extends Annotation> annotationClass;
private Class<?> markerInterface;
/**
* 将 Mapper 接口转换成 MapperFactoryBean 对象
*/
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
private String defaultScope;
}
basePackage
:Mapper 接口的包路径addToConfig
:是否要将接口添加到 Configuration 全局配置对象中sqlSessionFactoryBeanName
:SqlSessionFactory的Bean Name
上面这几个属性在 MapperScannerConfigurer 创建该对象的时候会进行赋值
registerFilters方法
registerFilters()
方法,添加几个过滤器,用于扫描 Mapper 接口的过程中过滤出我们需要的 Mapper 接口,方法如下:
public void registerFilters() {
// 标记是否接受所有的 Mapper 接口
boolean acceptAllInterfaces = true;
// if specified, use the given annotation and / or marker interface
// 如果配置了注解,则扫描有该注解的 Mapper 接口
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
// override AssignableTypeFilter to ignore matches on the actual marker interface
// 如果配置了某个接口,则也需要扫描该接口
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false;
}
if (acceptAllInterfaces) {
// default include filter that accepts all classes
// 如果上面两个都没有配置,则接受所有的 Mapper 接口
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
// 排除 package-info.java 文件
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
- 标记是否接受所有的接口
- 如果配置了注解,则添加一个过滤器,需要有该注解的接口
- 如果配置了某个接口,则添加一个过滤器,必须是该接口
- 如果没有第
2
、3
步,则添加一个过滤器,接收所有的接口 - 添加过滤器,排除 package-info.java 文件
doScan方法
doScan(String... basePackages)
方法,扫描指定包路径,根据上面的过滤器,获取路径下对应的 BeanDefinition 集合,进行一些后置处理,方法如下:
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 扫描指定包路径,根据上面的过滤器,获取到路径下 Class 对象的 BeanDefinition 对象
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
// 后置处理这些 BeanDefinition
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
- 扫描指定包路径,根据上面的过滤器,获取到路径下符合条件的 Resource 资源,并生成对应的
BeanDefinition
对象注册到 BeanDefinitionRegistry 注册表中,并返回 - 如果不为空,则调用
processBeanDefinitions
方法,进行一些后置处理
processBeanDefinitions方法
processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions)
方法,对指定包路径下符合条件的BeanDefinition
对象进行一些处理,修改其 Bean Class 为 MapperFactoryBean
类型,方法如下:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
// <1> 获取 BeanDefinition 注册表,然后开始遍历
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
boolean scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
// 获取被装饰的 BeanDefinition 对象
definition = (AbstractBeanDefinition) Optional
.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
.map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
"The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
scopedProxy = true;
}
// <2> 获取对应的 Bean 的 Class 名称,也就是 Mapper 接口的 Class 对象
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
// <3> 往构造方法的参数列表中添加一个参数,为当前 Mapper 接口的 Class 对象
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
/*
* <4> 修改该 Mapper 接口的 Class对象 为 MapperFactoryBean.class
* 这样一来当你注入该 Mapper 接口的时候,实际注入的是 MapperFactoryBean 对象,构造方法的入参就是 Mapper 接口
*/
definition.setBeanClass(this.mapperFactoryBeanClass);
// <5> 添加 addToConfig 属性
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
// <6> 开始添加 sqlSessionFactory 或者 sqlSessionTemplate 属性
/*
* 1. 如果设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 属性,实际上配置的是 SqlSessionFactoryBean 对象
* 2. 否则,如果配置了 sqlSessionFactory 对象,则添加 sqlSessionFactory 属性
*/
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
/*
* 1. 如果配置了 sqlSessionTemplateBeanName,则添加 sqlSessionTemplate 属性
* 2. 否则,如果配置了 sqlSessionTemplate 对象,则添加 sqlSessionTemplate 属性
* SqlSessionFactory 和 SqlSessionTemplate 都配置了则会打印一个警告
*/
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
// 如果上面已经清楚的使用了 SqlSessionFactory,则打印一个警告
LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
// 添加 sqlSessionTemplate 属性
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
/*
* 上面没有找到对应的 SqlSessionFactory,则设置通过类型注入
*/
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
if (scopedProxy) {
// 已经封装过的则直接执行下一个
continue;
}
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
/*
* 如果不是单例模式,默认是
* 将 BeanDefinition 在封装一层进行注册
*/
if (!definition.isSingleton()) {
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
registry.removeBeanDefinition(proxyHolder.getBeanName());
}
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
-
获取 BeanDefinition 注册表,然后开始遍历
-
获取对应的 Bean 的 Class 名称,也就是 Mapper 接口的 Class 对象
-
往构造方法的参数列表中添加一个参数,为当前 Mapper 接口的名称,因为 MapperFactoryBean 的构造方法的入参就是 Mapper 接口
-
修改该 Mapper 接口的 Class 对象 为
MapperFactoryBean
,根据第3
步则会为该 Mapper 接口创建一个对应的MapperFactoryBean
对象了 -
添加
addToConfig
属性,Mapper 是否添加到 Configuration 中 -
开始添加
sqlSessionFactory
或者sqlSessionTemplate
属性-
如果设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 属性,实际上配置的是 SqlSessionFactoryBean 对象,
否则,如果配置了 sqlSessionFactory 对象,则添加 sqlSessionFactory 属性
在
SqlSessionDaoSupport
的setSqlSessionFactory(SqlSessionFactory sqlSessionFactory)
方法中你会发现创建的就是SqlSessionTemplate
对象 -
如果配置了 sqlSessionTemplateBeanName,则添加 sqlSessionTemplate 属性
否则,如果配置了 sqlSessionTemplate 对象,则添加 sqlSessionTemplate 属性
-
上面没有找到对应的 SqlSessionFactory,则设置通过类型注入
-
该方法的处理逻辑大致如上描述,主要做了一下几个事:
-
将 Mapper 接口的 BeanDefinition 对象的 beanClass 属性修改成了
MapperFactoryBean
的 Class 对象 -
添加了一个入参为 Mapper 接口,这样初始化的 Spring Bean 就是该 Mapper 接口对应的
MapperFactoryBean
对象了 -
添加
MapperFactoryBean
对象的sqlSessionTemplate
属性
@MapperScan注解
org.mybatis.spring.annotation.@MapperScan
注解,指定需要扫描的包,将包中符合条件的 Mapper 接口,注册成 beanClass
为 MapperFactoryBean 的 BeanDefinition 对象,从而实现创建 Mapper 对象
我们代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
String lazyInitialization() default "";
String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT;
}
value
和basePackage
都是指定 Mapper 接口的包路径@Import(MapperScannerRegistrar.class)
,该注解负责资源的导入,如果导入的是一个 Java 类,例如此处为 MapperScannerRegistrar 类,Spring 会将其注册成一个 Bean 对象
MapperScannerRegistrar
org.mybatis.spring.annotation.MapperScannerRegistrar
:实现 ImportBeanDefinitionRegistrar、ResourceLoaderAware 接口
作为@MapperScann
注解的注册器,根据注解信息注册一个 MapperScannerConfigurer
对象,用于扫描 Mapper 接口
registerBeanDefinitions方法
registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
方法
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获得 @MapperScan 注解信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
// 生成 Bean 的名称,'org.springframework.core.type.AnnotationMetadata#MapperScannerRegistrar#0'
generateBaseBeanName(importingClassMetadata, 0));
}
}
- 获得 @MapperScan 注解信息
- 调用
generateBaseBeanName
方法,为MapperScannerConfigurer
生成一个beanName:org.springframework.core.type.AnnotationMetadata#MapperScannerRegistrar#0
- 调用
registerBeanDefinitions
重载方法,注册一个类型为MapperScannerConfigurer
的 BeanDefinition 对象
registerBeanDefinitions重载方法
registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName)
方法
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// 创建一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// 添加是否处理属性中的占位符属性
builder.addPropertyValue("processPropertyPlaceHolders", true);
/*
* 依次添加注解中的配置属性
*/
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
/*
* 获取到配置的 Mapper 接口的包路径
*/
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
/*
* 如果没有 Mapper 接口的包路径,则默认使用注解类所在的包路径
*/
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
String defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
// 添加 Mapper 接口的包路径属性
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// 往 BeanDefinitionRegistry 注册表注册 MapperScannerConfigurer 对应的 BeanDefinition 对象
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
- 创建一个 BeanDefinition 构建器,用于构建
MapperScannerConfigurer
的 BeanDefinition 对象 - 添加是否处理属性中的占位符属性
processPropertyPlaceHolders
- 依次添加
@MapperScan
注解中的配置属性,例如:sqlSessionFactoryBeanName
和basePackages
- 往 BeanDefinitionRegistry 注册表注册
MapperScannerConfigurer
类型的 BeanDefinition 对象
这样在 Spring 容器初始化的过程中,会创建一个 MapperScannerConfigurer 对象,然后回到MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法中,对包路径下的 Mapper 接口进行解析,前面已经分析过了
RepeatingRegistrar内部类
MapperScannerRegistrar的内部类,代码如下:
static class RepeatingRegistrar extends MapperScannerRegistrar {
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScansAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
if (mapperScansAttrs != null) {
// 获取 MapperScan 注解数组
AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");
/*
* 依次处理每个 MapperScan 注解
*/
for (int i = 0; i < annotations.length; i++) {
registerBeanDefinitions(importingClassMetadata, annotations[i], registry, generateBaseBeanName(importingClassMetadata, i));
}
}
}
}
- 可以回到
@MapperScan
注解上面的@Repeatable(MapperScans.class)
信息,可以看到如果同一个类上面定义多个@MapperScan
注解,则会生成对应的@MapperScans
注解 - RepeatingRegistrar 用于处理
@MapperScans
注解,依次处理@MapperScan
注解的信息 - 和 MapperScannerRegistrar 一样的处理方式,不过生成的多个 MapperScannerConfigurer 对应的 beanName 的后缀不一样
自定义 <mybatis:scan /> 标签
除了配置 MapperScannerConfigurer 对象和通过 @MapperScan 注解扫描 Mapper 接口以外,我们还可以通过 MyBatis 提供的 scan
标签来扫描 Mapper 接口
示例
<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />
spring.schemas
在 META-INF/spring.schemas
定义如下:
http\://mybatis.org/schema/mybatis-spring-1.2.xsd=org/mybatis/spring/config/mybatis-spring.xsd
http\://mybatis.org/schema/mybatis-spring.xsd=org/mybatis/spring/config/mybatis-spring.xsd
- xmlns 为
http://mybatis.org/schema/mybatis-spring-1.2.xsd
或http://mybatis.org/schema/mybatis-spring.xsd
- xsd 为 mybatis-spring.xsd
spring.handler
在 META-INF/spring.handlers
定义如下:
http\://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler
- 定义了 MyBatis 的 XML Namespace 的处理器 NamespaceHandler 对象
NamespaceHandler
org.mybatis.spring.config.NamespaceHandler
:继承 NamespaceHandlerSupport 抽象类,MyBatis 的 XML Namespace 的处理器,代码如下:
public class NamespaceHandler extends NamespaceHandlerSupport {
/**
* {@inheritDoc}
*/
@Override
public void init() {
registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());
}
}
<mybatis:scan />
标签,使用 MapperScannerBeanDefinitionParser 解析
MapperScannerBeanDefinitionParser
org.mybatis.spring.config.MapperScannerBeanDefinitionParser
:实现 BeanDefinitionParser 接口,<mybatis:scan />
的解析器,代码如下:
public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionParser {
private static final String ATTRIBUTE_BASE_PACKAGE = "base-package";
private static final String ATTRIBUTE_ANNOTATION = "annotation";
private static final String ATTRIBUTE_MARKER_INTERFACE = "marker-interface";
private static final String ATTRIBUTE_NAME_GENERATOR = "name-generator";
private static final String ATTRIBUTE_TEMPLATE_REF = "template-ref";
private static final String ATTRIBUTE_FACTORY_REF = "factory-ref";
private static final String ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS = "mapper-factory-bean-class";
private static final String ATTRIBUTE_LAZY_INITIALIZATION = "lazy-initialization";
private static final String ATTRIBUTE_DEFAULT_SCOPE = "default-scope";
/**
* 这个方法和 {@link MapperScannerRegistrar} 一样的作用
* 解析 <mybatis:scan /> 标签中的配置信息,设置到 MapperScannerConfigurer 的 BeanDefinition 对象中
* {@inheritDoc}
*
* @since 2.0.2
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// 创建一个 BeanDefinition 构建器,用于构建 MapperScannerConfigurer 的 BeanDefinition 对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
// 添加是否处理属性中的占位符属性
builder.addPropertyValue("processPropertyPlaceHolders", true);
/*
* 解析 `scan` 标签中的配置,添加到 BeanDefinition 中
*/
try {
String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);
if (StringUtils.hasText(annotationClassName)) {
@SuppressWarnings("unchecked")
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) classLoader
.loadClass(annotationClassName);
builder.addPropertyValue("annotationClass", annotationClass);
}
String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);
if (StringUtils.hasText(markerInterfaceClassName)) {
Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName);
builder.addPropertyValue("markerInterface", markerInterface);
}
String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);
if (StringUtils.hasText(nameGeneratorClassName)) {
Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);
BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);
builder.addPropertyValue("nameGenerator", nameGenerator);
}
String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS);
if (StringUtils.hasText(mapperFactoryBeanClassName)) {
@SuppressWarnings("unchecked")
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = (Class<? extends MapperFactoryBean>) classLoader
.loadClass(mapperFactoryBeanClassName);
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
} catch (Exception ex) {
XmlReaderContext readerContext = parserContext.getReaderContext();
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
}
builder.addPropertyValue("sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF));
builder.addPropertyValue("sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF));
builder.addPropertyValue("lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION));
builder.addPropertyValue("defaultScope", element.getAttribute(ATTRIBUTE_DEFAULT_SCOPE));
builder.addPropertyValue("basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE));
return builder.getBeanDefinition();
}
/**
* {@inheritDoc}
*
* @since 2.0.2
*/
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
}
- 代码的实现逻辑MapperScannerRegistrar一致,创建MapperScannerConfigurer对应的BeanDefinition对象,然后去解析
<mybatis:scan />
标签中的配置信息
SqlSessionTemplate
org.mybatis.spring.SqlSessionTemplate
:实现 SqlSession 和 DisposableBean 接口,SqlSession 操作模板实现类
实际上,代码实现和 org.apache.ibatis.session.SqlSessionManager
相似,承担 SqlSessionFactory 和 SqlSession 的职责
构造方法
public class SqlSessionTemplate implements SqlSession, DisposableBean {
/**
* a factory of SqlSession
*/
private final SqlSessionFactory sqlSessionFactory;
/**
* {@link Configuration} 中默认的 Executor 执行器类型,默认 SIMPLE
*/
private final ExecutorType executorType;
/**
* SqlSessionInterceptor 代理对象
*/
private final SqlSession sqlSessionProxy;
/**
* 异常转换器,MyBatisExceptionTranslator 对象
*/
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType,
new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 创建一个 SqlSession 的动态代理对象,代理类为 SqlSessionInterceptor
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
}
sqlSessionFactory
:用于创建 SqlSession 对象executorType
:执行器类型,创建 SqlSession 对象时根据它创建对应的 Executor 执行器,默认为sqlSessionProxy
:SqlSession 的动态代理对象,代理类为SqlSessionInterceptor
exceptionTranslator
:异常转换器
在调用SqlSessionTemplate中的SqlSession相关方法时,内部都是直接调用sqlSessionProxy
动态代理对象的方法,我们来看看是如何处理的
SqlSessionInterceptor
SqlSessionTemplate的内部类,实现了 InvocationHandler 接口,作为sqlSessionProxy
动态代理对象的代理类,对 SqlSession 的相关方法进行增强
代码如下:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// <1> 获取一个 SqlSession 对象
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// 执行 SqlSession 的方法
Object result = method.invoke(sqlSession, args);
// 当前 SqlSession 不处于 Spring 托管的事务中
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
// 强制提交
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
// 关闭 SqlSession 会话,释放资源
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
// 对异常进行转换,差不多就是转换成 MyBatis 的异常
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
- 调用
SqlSessionUtils
的getSqlSession
方法,获取一个 SqlSession 对象 - 执行 SqlSession 的方法
- 当前 SqlSession 不处于 Spring 托管的事务中,则强制提交
- 调用
SqlSessionUtils
的closeSqlSession
方法,“关闭”SqlSession 对象,这里的关闭不是真正的关闭
SqlSessionHolder
org.mybatis.spring.SqlSessionHolder
:继承 org.springframework.transaction.support.ResourceHolderSupport
抽象类,SqlSession 持有器,用于保存当前 SqlSession 对象,保存到 org.springframework.transaction.support.TransactionSynchronizationManager
中,代码如下:
public final class SqlSessionHolder extends ResourceHolderSupport {
/**
* SqlSession 对象
*/
private final SqlSession sqlSession;
/**
* 执行器类型
*/
private final ExecutorType executorType;
/**
* PersistenceExceptionTranslator 对象
*/
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionHolder(SqlSession sqlSession, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSession, "SqlSession must not be null");
notNull(executorType, "ExecutorType must not be null");
this.sqlSession = sqlSession;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
}
}
- 当存储到 TransactionSynchronizationManager 中时,使用的 KEY 为创建该 SqlSession 对象的 SqlSessionFactory 对象,后续会分析
SqlSessionUtils
org.mybatis.spring.SqlSessionUtils
:SqlSession 工具类,负责处理 MyBatis SqlSession 的生命周期,借助 Spring 的 TransactionSynchronizationManager 事务管理器管理 SqlSession 对象
getSqlSession方法
getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)
方法,注释如下:
Gets an SqlSession from Spring Transaction Manager or creates a new one if needed. Tries to get a SqlSession out of current transaction.
If there is not any, it creates a new one. Then, it synchronizes the SqlSession with the transaction if Spring TX is active and SpringManagedTransactionFactory is configured as a transaction manager.
从事务管理器(线程安全)中获取一个 SqlSession 对象,如果不存在则创建一个 SqlSession,然后注册到事务管理器中,方法如下:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// 从 Spring 事务管理器中获取一个 SqlSessionHolder 对象
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 获取到 SqlSession 对象
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
LOGGER.debug(() -> "Creating a new SqlSession");
// 上面没有获取到,则创建一个 SqlSession
session = sessionFactory.openSession(executorType);
// 将上面创建的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
-
从 Spring 事务管理器中,根据 SqlSessionFactory 获取一个 SqlSessionHolder 对象
-
调用
sessionHolder
方法,获取到 SqlSession 对象,方法如下private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { SqlSession session = null; if (holder != null && holder.isSynchronizedWithTransaction()) { // 如果执行器类型发生了变更,抛出 TransientDataAccessResourceException 异常 if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException( "Cannot change the ExecutorType when there is an existing transaction"); } // 增加计数,关闭 SqlSession 时使用 holder.requested(); LOGGER.debug(() -> "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"); // 获得 SqlSession 对象 session = holder.getSqlSession(); } return session; }
-
如果 SqlSession 对象不为 null,则直接返回,接下来会创建一个
-
上面没有获取到,则创建一个 SqlSession 对象
-
调用
registerSessionHolder
方法,将上面创建的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册 -
返回新创建的 SqlSession 对象
registerSessionHolder方法
registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session)
方法
注释如下:
Register session holder if synchronization is active (i.e. a Spring TX is active).
Note: The DataSource used by the Environment should be synchronized with the transaction either through DataSourceTxMgr or another tx synchronization.
Further assume that if an exception is thrown, whatever started the transaction will handle closing / rolling back the Connection associated with the SqlSession.
如果事务管理器处于激活状态,则将 SqlSession 封装成 SqlSessionHolder 对象注册到其中,方法如下:
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
// <1> 如果使用 Spring 事务管理器
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");
// <1.1> 创建 SqlSessionHolder 对象
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
// <1.2> 绑定到 TransactionSynchronizationManager 中
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
// <1.3> 创建 SqlSessionSynchronization 到 TransactionSynchronizationManager 中
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
// <1.4> 设置同步
holder.setSynchronizedWithTransaction(true);
// <1.5> 增加计数
holder.requested();
} else {
// <2> 如果非 Spring 事务管理器,抛出 TransientDataAccessResourceException 异常
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
LOGGER.debug(() -> "SqlSession [" + session
+ "] was not registered for synchronization because DataSource is not transactional");
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} else {
LOGGER.debug(() -> "SqlSession [" + session
+ "] was not registered for synchronization because synchronization is not active");
}
}
- 如果使用 Spring 事务管理器,才会进行注册
- 创建 SqlSessionHolder 对象
holder
- 绑定到 TransactionSynchronizationManager 中,key 为 SqlSessionFactory 对象
- 创建
SqlSessionSynchronization
对象(事务同步器)到 TransactionSynchronizationManager 中 - 设置
holder
的synchronizedWithTransaction
属性为ture,和事务绑定了 - 增加
holder
的referenceCount
引用数量
closeSqlSession方法
closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)
方法,注释如下:
Checks if SqlSession passed as an argument is managed by Spring TransactionSynchronizationManager
If it is not, it closes it, otherwise it just updates the reference counter and lets Spring call the close callback when the managed transaction ends
如果 SqlSessionFactory 是由 Spring 的事务管理器管理,并且和入参中的 session
相同,那么只进行释放,也就是将 referenceCount
引用数量减一,否则就直接关闭了
方法如下:
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
// <1> 从 TransactionSynchronizationManager 中,获得 SqlSessionHolder 对象
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// <2.1> 如果相等,说明在 Spring 托管的事务中,则释放 holder 计数
if ((holder != null) && (holder.getSqlSession() == session)) {
LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
holder.released();
} else {
// <2.2> 如果不相等,说明不在 Spring 托管的事务中,直接关闭 SqlSession 对象
LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
session.close();
}
}
- 从事务管理器中,根据 SqlSessionFactory 获得 SqlSessionHolder 对象
- 如果相等,说明在 Spring 托管的事务中,则释放 holder 计数
- 否则,不在 Spring 托管的事务中,直接关闭 SqlSession 对象
isSqlSessionTransactional方法
isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory)
方法,判断 SqlSession 对象是否被 Sping 的事务管理器管理,代码如下:
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
notNull(session, NO_SQL_SESSION_SPECIFIED);
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
// 从 TransactionSynchronizationManager 中,获得 SqlSessionHolder 对象
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 如果相等,说明在 Spring 托管的事务中
return (holder != null) && (holder.getSqlSession() == session);
}
SqlSessionSynchronization
org.mybatis.spring.SqlSessionUtils
的内部类,继承了 TransactionSynchronizationAdapter 抽象类,SqlSession 的事务同步器,基于 Spring Transaction 体系
注释如下:
Callback for cleaning up resources.
It cleans TransactionSynchronizationManager and also commits and closes the SqlSession.
It assumes that Connection life cycle will be managed by DataSourceTransactionManager or JtaTransactionManager
回调的时候清理资源
构造方法
private static final class SqlSessionSynchronization extends TransactionSynchronizationAdapter {
private final SqlSessionHolder holder;
private final SqlSessionFactory sessionFactory;
/**
* 是否开启
*/
private boolean holderActive = true;
public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sessionFactory) {
notNull(holder, "Parameter 'holder' must be not null");
notNull(sessionFactory, "Parameter 'sessionFactory' must be not null");
this.holder = holder;
this.sessionFactory = sessionFactory;
}
}
getOrder方法
@Override
public int getOrder() {
// order right before any Connection synchronization
return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1;
}
suspend方法
当事务挂起时,取消当前线程的绑定的 SqlSessionHolder 对象,方法如下:
@Override
public void suspend() {
if (this.holderActive) {
LOGGER.debug(() -> "Transaction synchronization suspending SqlSession [" + this.holder.getSqlSession() + "]");
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
}
resume方法
当事务恢复时,重新绑定当前线程的 SqlSessionHolder 对象,方法如下:
@Override
public void resume() {
if (this.holderActive) {
LOGGER.debug(() -> "Transaction synchronization resuming SqlSession [" + this.holder.getSqlSession() + "]");
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.holder);
}
}
beforeCommit方法
在事务提交之前,调用 SqlSession#commit() 方法之前,提交事务。虽然说,Spring 自身也会调用 Connection#commit() 方法,进行事务的提交。但是,SqlSession#commit() 方法中,不仅仅有事务的提交,还有提交批量操作,刷新本地缓存等等,方法如下:
@Override
public void beforeCommit(boolean readOnly) {
// Connection commit or rollback will be handled by ConnectionSynchronization or DataSourceTransactionManager.
// But, do cleanup the SqlSession / Executor, including flushing BATCH statements so they are actually executed.
// SpringManagedTransaction will no-op the commit over the jdbc connection
// TODO This updates 2nd level caches but the tx may be rolledback later on!
if (TransactionSynchronizationManager.isActualTransactionActive()) {
try {
LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
// 提交事务
this.holder.getSqlSession().commit();
} catch (PersistenceException p) {
// 如果发生异常,则进行转换,并抛出异常
if (this.holder.getPersistenceExceptionTranslator() != null) {
DataAccessException translated = this.holder.getPersistenceExceptionTranslator()
.translateExceptionIfPossible(p);
if (translated != null) {
throw translated;
}
}
throw p;
}
}
}
beforeCompletion方法
提交事务完成之前,关闭 SqlSession 对象,在 beforeCommit 之后调用,方法如下:
@Override
public void beforeCompletion() {
// Issue #18 Close SqlSession and deregister it now
// because afterCompletion may be called from a different thread
if (!this.holder.isOpen()) {
LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
// 取消当前线程的绑定的 SqlSessionHolder 对象
TransactionSynchronizationManager.unbindResource(sessionFactory);
// 标记无效
this.holderActive = false;
LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
// 关闭 SqlSession 对象
this.holder.getSqlSession().close();
}
}
afterCompletion方法
在事务完成之后,关闭 SqlSession 对象,解决可能出现的跨线程的情况,方法如下:
@Override
public void afterCompletion(int status) {
if (this.holderActive) { // 处于有效状态
// afterCompletion may have been called from a different thread
// so avoid failing if there is nothing in this one
LOGGER.debug(() -> "Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");
// 取消当前线程的绑定的 SqlSessionHolder 对象
TransactionSynchronizationManager.unbindResourceIfPossible(sessionFactory);
// 标记无效
this.holderActive = false;
LOGGER.debug(() -> "Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");
// 关闭 SqlSession 对象
this.holder.getSqlSession().close();
}
this.holder.reset();
}
总结
还有一部分内容在org.mybatis.spring.batch
包路径下,基于 Spring Batch 框架,Spring 和 MyBatis 的批处理进行集成,感兴趣的小伙伴可以去阅读一下
我通常都是通过配置示例中方式配置MyBatis的,因为我觉得配置文件易于维护,比较可观,当然也通过注解(@MapperScan
)的方式进行配置,原理相同
-
首先配置
DataSource
数据源的 Sping Bean,我们通常不会使用 MyBatis 自带的数据源,因为其性能不好,都是通过Druid
或者HikariCP
等第三方组件来实现 -
配置
SqlSessionFactoryBean
的 Spring Bean,设置数据源属性dataSource
,还可以配置configLocation
(mybatis-config.xml配置文件的路径)、mapperLocations
(XML映射文件的路径)等属性,这样让 Spring 和 MyBatis 完美的整合到一起了 -
配置
MapperScannerConfigurer
的 Spring Bean,设置basePackage
(需要扫描的Mapper接口的路径)、sqlSessionFactoryBeanName
(上面定义的SqlSessionFactoryBean)等属性因为实现了
BeanDefinitionRegistryPostProcessor
接口,在这些 Mapper 接口的 BeanDefinition 对象(Spring Bean 的前身)注册完毕后,可以进行一些处理在这里会修改这些 BeanDefinition 对象为
MapperFactoryBean
类型,在初始化 Spring Bean 的过程中则创建的是 MapperFactoryBean 对象,注入该对象则会调用其getObject()
方法,返回的该 Mapper 接口对应的动态代理对象这样当你注入 Mapper 接口时,实际注入的是其动态代理对象
-
在
SqlSessionTemplate
对象中,承担 SqlSessionFactory 和 SqlSession 的职责
到这里,相信大家对 MyBatis 集成到 Spring 的方案有了一定的了解,感谢大家的阅读!!!😄😄😄
参考文章:芋道源码《精尽 MyBatis 源码分析》