1. 目录

[TOC]

2. 说明

本文分析mybatis在springboot 启动条件下的启动流程

3. 依赖分析

maven依赖

项目启动依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<!--mybatis启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--mysql连接,没有这个spring提供的的Datasource是不会创建的-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>

mybatis-sring-boot-starter 内部依赖

mybatis-sring-boot-starter主要是做一个整合,其本身没有功能,只有一个spring.provider配置provides: mybatis-spring-boot-autoconfigure,mybatis,mybatis-spring

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
<dependencies>
<!--springboot固有的,这个里面也没啥东西,没有类也没有配置,只有说明文件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--jdbc相关,数据库连接-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mybatis自动化配置,很重要,自动化加载mabatis的configuration和sqlsessionFactory-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<!--mybatis的核心包,在没有spring的情况下都是可以使用的-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!--mybatis融入spring,这个在spring下也是核心-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>

mybatis-spring-boot-autoconfigure
mybatis-spring-boot-autoconfigure

mybatis-spring mybatis
mybatis-spring mybatis

4. 源码分析

源码就直接从mybatis-spring-boot-autoconfigure开始,这里开始定义bean

AutoConfiguredMapperScannerRegistrar 配置扫描

AutoConfiguredMapperScannerRegistrar 实现了ImportBeanDefinitionRegistrar,在beanDifinition阶段会执行registerBeanDefinitions方法定义扫描的信息

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
//AutoConfiguredMapperScannerRegistrar
//主要是ImportBeanDefinitionRegistrar的实现,另外两个就是单纯的注入工厂
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {

private BeanFactory beanFactory;

private ResourceLoader resourceLoader;

//主要是这个方法,注册BeanDifinition
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

logger.debug("Searching for mappers annotated with @Mapper");

//构造mybatis自己的扫描
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}

//获取到spingboot的扫描包路径(与maybaits无关),一般为我们的工程或者@ComponentScan扫描的
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
for (String pkg : packages) {
logger.debug("Using auto-configuration base package '{}'", pkg);
}
}
//要解析的注解, 这里是@Mapper
scanner.setAnnotationClass(Mapper.class);
//注册过滤器链,添加@Mapper注解的解析
scanner.registerFilters();
//执行扫描,这里主要是扫描@Mapper的接口
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}
}

ClassPathMapperScanner#registerFilters() 注册拦截器链

这是mybatis-spring的方法

1
2
3
4
5
6
7
8
9
10
public void registerFilters() {
boolean acceptAllInterfaces = true;

//添加注解过滤器到filter中,然后返回
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
//省略不重要的......
}

ClassPathMapperScanner#doScan() 执行扫描

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
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//获取到扫描包下的所有@Mapper接口,并生成beanDefinition
// 这里是spring的通用注解扫描类方法,扫描出包下的与前面设置的`scanner.setAnnotationClass(Mapper.class)`匹配的注解对应的类
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;
}
//处理bean定义
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {

//配置BeanDifinition参数
definition = (GenericBeanDefinition) holder.getBeanDefinition();

//设置构造器参数,这个很重要,后面spring初始化的时候会根据此参数传入构造器实例化对象(找了我大半天,原来就是在这里设置的)
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//设置实例的类型,mybatis所有的Mapper都实例化MapperFactoryBean代理类,在代理类getObject()的时候创建spring的bean实例
definition.setBeanClass(this.mapperFactoryBean.getClass());


definition.getPropertyValues().add("addToConfig", this.addToConfig);
//设置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;
}
//设置sqlSessionTemplate,默认不走,后面会单独创建
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
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;
}

if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}

MybatisAutoConfiguration 配置

此配置主要创建SqlSessionFactorySqlSessionTemplate

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
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
//构造器加载容器中有的实例
public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
}

//创建 SqlSessionFactory 工厂;mybatis-spring中很重要的
//需要传入数据源
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();

//设置数据源
factory.setDataSource(dataSource);

//配置文件配置
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
//自定义逻辑,可以自己在configuration中添加
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
//工厂中添加配置
factory.setConfiguration(configuration);

if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
//执行工厂的配置及返回,总的就是将现在设置的组装返回出去
return factory.getObject();
}

// SqlSessionTemplate 数据库连接
// 传入工厂, 返回组装数据库连接
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
}

Mapper实例创建及初始化

前面扫描包的时候已经将扫描到的Mapper类通过封装MapperFactoryBean加载到BeanDifnition中,接下来我们开初始化Bean的过程

创建实例工厂

spring在createBean()的时候主要通过createBeanInstance()方法创建bean,内部调用autowireConstructor()方法通过构造器创建实例,具体的spring源码我们不分析;
autowireConstructor有一系列方法处理RootBeanDefinition中定义的参数,其中就有实例化definition.setBeanClass(this.mapperFactoryBean.getClass()),把前面设置的构造器参数definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName())传入,然后调用以下方法

1
2
3
4
5
6
7
// MapperFactoryBean.java
//MapperFactoryBean 构造器
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
}

mapperInterface 即为我们定义的Mapper的class类型

填充属性

创建了实例之后,由于之前的BeanDifinition中设置了自动注入的类型为AUTOWIRED_BY_TYPE,则会调用对象的set方法将容器中已有的bean注入进对象中,而MapperFactoryBean包含了setSqlSessionFactory()setSqlSessionTemplate两个自动注入,因此会把我们配置的已经被容器初始化的两个对象注入进来,如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}

public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
}
}

上面两个不管谁先执行,实际上最终注入的是启动配置中配置好的SqlSessionTemplate

同时,在afterPropertiesSet中会创建一个MapperProxyFactory加入到mybatis中的Configurare中

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
//MapperFactoryBean的父类
public abstract class DaoSupport implements InitializingBean {
@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {

//看实现类MapperFactoryBean
checkDaoConfig();

try {
//空方法
initDao();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}
}
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
protected void checkDaoConfig() {
//......省略检查是否存在sqlsession和mapperInterface

//获取mybatis配置对象Configuration 并加入Mapper
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
//configuration内部就不看了,就是把当前类封装一个MapperProxyFactory加入到knownMappers中
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();
}
}
}
}

创建真正的代理Bean

在后续spring 获取bean并加入容器的时候,会调用doGetBean()-> getObjectForBeanInstance(),内部判定这是一个FactoryBean,则会调用其getObject()方法获取真正的实例

1
2
3
4
5
6
7
8
9
// MapperFactoryBean.java
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

@Override
public T getObject() throws Exception {
//getSqlSession获取属性对象SqlSession,
return getSqlSession().getMapper(this.mapperInterface);
}
}

SqlSessionTemplate#getMapper()

sqlSession中配置有sqlSessionFactory,其内部配置了mybatis的Configuration,而Configuration中MapperRegistry会通过MapperProxyFactory创建一个MapperProxy实例

  • SqlSessionTemplate#getMapper()

    1
    2
    3
    public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
    }
  • Configuration#getMapper()

    1
    2
    3
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
    }
  • MapperRegistry#getMapper()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //这里从knownMappers拿到类型对应的MapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
    //这里创建新的 MapperProxy jdk动态代理对象代理原来的接口
    return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
    }

5. 总结

mybatis启动的主要流程就是在SqlSessionFactorySqlSessionTemplate的生成和MapperBean的初始化过程

    1. SqlSessionFactory在MybatisAutoConfiguration中定义。其中configurar配置根据MybatisProperties逐个加入到ConfigurarBuilder中再加入到SqlSessionFactory
    1. SqlSessionTemplate也在MybatisAutoConfiguration中定义,主要把SqlSessionFactory加入,之后实例化的时候用到
    1. Mapper的生成比较复杂,在BeanDifinition阶段会设置实例的class为MapperFactoryBean,并且把原Mapper加入到BeanDifnition的构造器属性中,在初始化Bean的时候会通过构造器注入Mapper类对象
    1. 初始化MapperFactoryBean的时候会加入Sqlsession,并在初始化方法afterProperties中将此类对应封装的代理类MapperProxyFactory加入到Configuration中,但是MapperProxyFactory此时不创建代理类
    1. MapperFactoryBeangetObject的时候会调用MapperProxyFactory#newInstance()创建jdk动态代理类

6. 注意事项