1. 目录 [TOC]
2. 说明 本文分析mybatis在springboot 启动条件下的启动流程
3. 依赖分析 maven依赖 项目启动依赖 1 2 3 4 5 6 7 8 9 10 11 12 <dependencies > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > </dependency > <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 > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-jdbc</artifactId > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-autoconfigure</artifactId > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > </dependency > </dependencies >
mybatis-spring-boot-autoconfigure
mybatis-spring
mybatis
4. 源码分析 源码就直接从mybatis-spring-boot-autoconfigure
开始,这里开始定义bean
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 public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware , ImportBeanDefinitionRegistrar, ResourceLoaderAware { private BeanFactory beanFactory; private ResourceLoader resourceLoader; @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { logger.debug("Searching for mappers annotated with @Mapper" ); ClassPathMapperScanner scanner = new ClassPathMapperScanner (registry); try { if (this .resourceLoader != null ) { scanner.setResourceLoader(this .resourceLoader); } List<String> packages = AutoConfigurationPackages.get(this .beanFactory); if (logger.isDebugEnabled()) { for (String pkg : packages) { logger.debug("Using auto-configuration base package '{}'" , pkg); } } scanner.setAnnotationClass(Mapper.class); scanner.registerFilters(); 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 ; 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) { 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 { processBeanDefinitions(beanDefinitions); } return beanDefinitions; } private void processBeanDefinitions (Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); definition.setBeanClass(this .mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig" , this .addToConfig); 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 ; } 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
配置此配置主要创建SqlSessionFactory
和SqlSessionTemplate
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(); } @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)) { 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(); } @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 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 public abstract class DaoSupport implements InitializingBean { @Override public final void afterPropertiesSet () throws IllegalArgumentException, BeanInitializationException { 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 () { Configuration configuration = getSqlSession().getConfiguration(); if (this .addToConfig && !configuration.hasMapper(this .mapperInterface)) { try { 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 public class MapperFactoryBean <T> extends SqlSessionDaoSupport implements FactoryBean <T> { @Override public T getObject () throws Exception { 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) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null ) { throw new BindingException ("Type " + type + " is not known to the MapperRegistry." ); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException ("Error getting mapper instance. Cause: " + e, e); } }
5. 总结 mybatis启动的主要流程就是在SqlSessionFactory
、SqlSessionTemplate
的生成和Mapper
Bean的初始化过程
SqlSessionFactory
在MybatisAutoConfiguration中定义。其中configurar配置根据MybatisProperties
逐个加入到ConfigurarBuilder
中再加入到SqlSessionFactory
SqlSessionTemplate
也在MybatisAutoConfiguration中定义,主要把SqlSessionFactory
加入,之后实例化的时候用到
Mapper
的生成比较复杂,在BeanDifinition阶段会设置实例的class为MapperFactoryBean
,并且把原Mapper
加入到BeanDifnition的构造器属性中,在初始化Bean的时候会通过构造器注入Mapper类对象
初始化MapperFactoryBean
的时候会加入Sqlsession
,并在初始化方法afterProperties
中将此类对应封装的代理类MapperProxyFactory
加入到Configuration
中,但是MapperProxyFactory
此时不创建代理类
在MapperFactoryBean
中getObject
的时候会调用MapperProxyFactory#newInstance()
创建jdk动态代理类
6. 注意事项