Mybatis 源码整合 Spring

在使用 Mybatis 时, 需要通过 SqlSessionFactory 打开一个SqlSession, 然后实例化 Mapper 接口的动态代理对象, 执行数据库相关操作。

创建 SqlSessionFactory 对象

1
2
3
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建 SqlSessionFactorySqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口,Spring 将会在应用启动时为你创建 SqlSessionFactory,并使用 sqlSessionFactory 这个名字存储起来。

定位: org.mybatis.spring.SqlSessionFactoryBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// Spring 初始化 bean 时, 会被调用
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
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();
}
// 生成 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 {
// 解析 xml 配置文件
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));

if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
/*
* 配置了 XML 映射文件的路径, 遍历所有的 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);
}
// Spring 事件监听
public void onApplicationEvent(ApplicationEvent event) {
// 需要快速失败,并且监听到了 Spring 容器初始化完成事件
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
// 将 MyBatis 中还未完全解析的对象,在这里再进行解析
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
// 扫描指定包下的指定子类或者子接口, 如果 assignableType 为空, 则返回这个包下的所有类
private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {
Set<Class<?>> classes = new HashSet<>();
String[] packagePatternArray = tokenizeToStringArray(packagePatterns,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packagePattern : packagePatternArray) {
// 获取到 classpath 下包路径下的 Resource 对象
Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
for (Resource resource : resources) {
try {
// 获取 Resource 资源的 ClassMetadata 对象
ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
if (assignableType == null || assignableType.isAssignableFrom(clazz)) {
classes.add(clazz);
}
} catch (Throwable e) {
LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString());
}
}
}
return classes;
}
}

实例化 Mapper 接口

扫描 Mapper

通过定义 MapperScannerConfigurer 的 Spring bean 对象

1
2
3
4
5
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.spring.sample.mapper" />
<!-- 扫描被 Mapper 注解修饰的类(如果在 basePackage 包下存在非 Mapper 接口的文件, 并且不想要被代理, 则需要设置) -->
<property name="annotationClass" value="org.apache.ibatis.annotations.Mapper" />
</bean>
MapperScannerConfigurer

定位: org.mybatis.spring.mapper.MapperScannerConfigurer

作用:用于扫描 Mapper 接口,借助ClassPathMapperScanner扫描器注册 Mapper 接口的BeanDefinition 对象 (注意: 默认情况并没有设置 annotationClass )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
/**
* 在 BeanDefinitionRegistry 完成后, 扫描 basePackage 下的类
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
// 处理属性中的占位符
processPropertyPlaceHolders();
}
// 创建一个 Bean 扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 是否要将 Mapper 接口添加到 Configuration 全局配置对象中
scanner.setAddToConfig(this.addToConfig);
// 如果 annotationClass 不为空,则扫描被 annotationClass 注解修饰的类
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);
}
// 会根据 annotationClass, markerInterface 生成过滤器
// 添加几个过滤器
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
ClassPathMapperScanner

定位: org.mybatis.spring.mapper.ClassPathMapperScanner

作用: 负责执行扫描,将用户配置信息,设置到扫描到的 Mapper 接口的 BeanDefinition 对象,如将 BeanClass 修改为 MapperFactoryBean,将 autowireMode 设置为根据类型注入,可以让 Spring 实例化的时候,会使用 MapperFactoryBean 类型,并自动注入 SqlSessionFactory 实例,实现创建 Mapper 动态代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public void registerFilters() {
// 标记是否接受所有接口
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
// 如果上面两个都没有配置,则接受所有的类
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}

// exclude package-info.java
// 排除 package-info.java 文件
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
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;
}

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 属性
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
// 设置了 sqlSessionFactoryBeanName,则添加 sqlSessionFactory 对象的引用
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
// 配置了 sqlSessionFactory 对象,则设置 sqlSessionFactory 属性值
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}

if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
// 如果上面已经清楚的使用了 SqlSessionFactory,则打印一个警告
LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
// 配置了 sqlSessionTemplateBeanName,则添加 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.");
}
// 配置了 sqlSessionTemplate 对象,则设置 sqlSessionTemplate 属性值
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}

/*
* 上面没有找到对应的 SqlSessionFactory,则设置通过类型注入
*/
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
// Mapper 接口初始化中会自动根据类型注入 SqlSessionFactory 的实例
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());
}
}
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// 必须是接口, 并且是顶级了或者内部类
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}

通过注解 @MapperScan 扫描

使用例子:

1
2
3
4
@Configuration  
@MapperScan(value = "org.mybatis.spring.sample.mapper", annotationClass = Mapper.class)
public class AppConfig {
}
MapperScan 注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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;
}
MapperScannerRegistrar

定位: org.mybatis.spring.annotation.MapperScannerRegistrar

作用: @MapperScann 的注册器,根据注解信息注册 MapperScannerConfigurer 对象,用于扫描 Mapper 接口

1
2
3
4
5
6
7
8
9
10
11
12
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 将 MapperScan 的注解信息封装成 MapperScannerConfigurer BeanDefinition 对象, 并注册到 Spring 中
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
}

通过 Mybatis-Spring 的 xml 配置(基本不用)

1
2
3
4
5
6
7
8
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<mybatis:scan base-package="org.mybatis.spring.sample.mapper" />
</beans>

<mybatis:scan /> 的解析器 org.mybatis.spring.config.MapperScannerBeanDefinitionParser, 实现原理等同于 MapperScannerRegistrar, 解析 mybatis:scan 配置的标签信息并注册 MapperScannerConfigurer 对象

实例化 Mapper

MapperFactoryBean

定位: org.mybatis.spring.mapper.MapperFactoryBean

作用: 实现 FactoryBean 接口,继承 SqlSessionDaoSupport 抽象类,Mapper 接口对应 Spring Bean 对象,用于返回对应的动态代理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
/**
* 继承了 DaoSupport 接口,它实现了 InitializingBean 接口
* 则在 Spring 容器中初始化该 Bean 的 afterPropertiesSet() 方法,也就调用该方法
*/
protected void checkDaoConfig() {
// 校验 sqlSessionTemplate 非空
super.checkDaoConfig();
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();
}
}
}
/**
* 在 Spring 容器中,注入当前 Bean 时调用该方法,也就是返回 Mapper 接口的动态代理对象(代理类为{@link org.apache.ibatis.binding.MapperProxy})
*/
public T getObject() throws Exception {
// getSqlSession() 方法返回 SqlSessionTemplate 对象
return getSqlSession().getMapper(this.mapperInterface);
}
}

SqlSessionDaoSupport

定位: org.mybatis.spring.support.SqlSessionDaoSupport

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
// 设置 DAO 使用的 MyBatis SqlSessionFactory, 并根据给定的 SqlSessionFactory 创建 SqlSessionTemplate 对象
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
// 根据 sqlSessionFactory 创建 SqlSessionTemplate 对象
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
// 显式设置 DAO 的 SqlSessionTemplate,作为指定 SqlSessionFactory 的替代方法
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
}

SqlSessionTemplate

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。 SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class SqlSessionTemplate implements SqlSession, DisposableBean {

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());
}
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
}
@Override
public int update(String statement, Object parameter) {
return this.sqlSessionProxy.update(statement, parameter);
}
// 。。。
// 基本所有 SqlSession 接口的方法, 都由 sqlSessionProxy 来进行调用
// 。。。
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}

// JDK 代理 Handler
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取 SqlSession 对象
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// 执行 SqlSession 的方法
Object result = method.invoke(sqlSession, args);
// 当前 SqlSession 不是 Spring 托管的事务
if (!SqlSessionUtils.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 会话,释放资源
SqlSessionUtils.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) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
}

获取当前的 sqlSession

定位: org.mybatis.spring.SqlSessionUtils#getSqlSession

作用: 从当前线程变量中获取 SqlSession,如果不存在,则创建一个, 并将新创建的 SQL Session 添加到线程变量中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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;
}
// 上面没有获取到,则创建一个 SqlSession
session = sessionFactory.openSession(executorType);
// 将上面创建的 SqlSession 封装成 SqlSessionHolder,往 Spring 事务管理器注册
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
// 将 sqlSession 对象注册到当前线程变量中
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");
}
}

SqlSession 和 Mapper 关系

在 Mybatis 中, 每次打开 openSession 后, 都会创建一个新的 SqlSession 实例, 在从 SqlSession 中获取 Mapper 实例时, 也会创建一个新的 Mapper 代理对象;

在 Mybatis-Spring 中, Mapper 的代理对象由 Spring 容器的 MapperFactoryBean 对象创建, 并且为单例的,每次调用 Mapper 的增删改查时,会先 openSession ( 创建一个新 SqlSession 实例) 后, 通过 ThreadLocal 来实现当前线程持有当前的 SqlSession 实例对象

Mybatis-Spring 事务托管

使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。

一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。

1
2
3
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>

编程式事务管理案例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserService {
private final PlatformTransactionManager transactionManager;
public UserService(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void createUser() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(txStatus -> {
userMapper.insertUser(user);
return null;
});
}
}

链接

https://mybatis.org/spring/zh/index.html

mybatis-spring 注释源码