一、配置類導入
1、mybatis-spring-boot-starter 引入了如下圖5個依賴
spring-boot-starter是每個starter都要引入的
spring-boot-starter-jdbc 與jdbc相關
后面兩個mybatis, mybatis -spring 與mybatis相關
mybatis-spring-boot-autoconfigure 根據之前自定義的starter,它里面spring.factories有一個配置類實現了
2、進入MyBatisAutoConfiguration類
1)第一個注解是Configuration,標注這個類是配置類
2)接下類是ConditionalOnClass注解,要求容器里有SqlSessionFactory類和SqlSessionfactoryBean類
3)ConditionalOnSingleCandidate注解:要求容器中存在DataSource類
4)接着使用EnableConfigurationProperties注解使MybatisProperties這個類生效。
進入MybatisProperties類
獲得mybatis前綴的屬性
5)@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
說明當前類要在DataSourceAutoConfiguration和MybatisLanguageDriverAutoConfiguration這兩個類之后進行裝載
a) DataSourceAutoConfiguration
這個類是對數據源進行配置的
DataSourceProperties獲得spring.datasource 的屬性
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
二、關鍵類注入
MyBatisAutoConfiguration類會注入兩個重要的Bean,分別為SqlSessionFactory和sqlSessionTemplate
1、首先進入SqlSessionFactory這個bean方法
sqlSessionFactory是單個數據庫映射關系經編譯后的內存鏡像
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if(StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } this.applyConfiguration(factory); 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(this.properties.getTypeAliasesSuperType() != null) { factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType()); } if(StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if(!ObjectUtils.isEmpty(this.typeHandlers)) { factory.setTypeHandlers(this.typeHandlers); } if(!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet()); Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); if(factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers); if(defaultLanguageDriver == null && this.languageDrivers.length == 1) { defaultLanguageDriver = this.languageDrivers[0].getClass(); } } if(factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); } return factory.getObject(); }
2、sqlSessionTemplate類
執行數據庫操作的工具類
@Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); return executorType != null?new SqlSessionTemplate(sqlSessionFactory, executorType):new SqlSessionTemplate(sqlSessionFactory); }
進入SqlSessionTemplate類,
里面有SqlSessionFactory和sqlSessionProxy
sqlSessionProxy是代理類,里面的增刪改查都是通過它來執行的。處理方法都在SqlSessionInterceptor里面
3、SqlSessionInterceptor 類如下
private class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped; try { Object result = method.invoke(sqlSession, args); if(!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } unwrapped = result; } catch (Throwable var11) { unwrapped = ExceptionUtil.unwrapThrowable(var11); if(SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped); if(translated != null) { unwrapped = translated; } } throw (Throwable)unwrapped; } finally { if(sqlSession != null) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } return unwrapped; } }
1) 進入getSqlSession這個方法
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sessionFactory, "No SqlSessionFactory specified"); Assert.notNull(executorType, "No ExecutorType specified"); SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if(session != null) { return session; } else { LOGGER.debug(() -> { return "Creating a new SqlSession"; }); session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } }
a) 通過資源管理器獲得資源 (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
getResource如下。里面調用了doGetRecource
@Nullable public static Object getResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Object value = doGetResource(actualKey); if(value != null && logger.isTraceEnabled()) { logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } return value; }
進入doGetResource
@Nullable private static Object doGetResource(Object actualKey) { Map<Object, Object> map = (Map)resources.get(); if(map == null) { return null; } else { Object value = map.get(actualKey); if(value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) { map.remove(actualKey); if(map.isEmpty()) { resources.remove(); } value = null; } return value; } }
resource的數據結構如下
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
三、mapper類掃描
1、進入MybatisAutoConfiguration類。
因為沒有mapperFactoryBean和MapperScannerConfigurer類,所有會把MapperScannerRegisterarNoFoundConfiguration 注入到容器中,並且導入AutoConfiguredMapperScannerRegistrar類
2、進入AutoConfiguredMapperScannerRegistrar類。
實現了ImportBanDefinitionRegistrar接口
最后注冊MapperScannerConfigurer這個Bean。
3、MapperScannerConfigurer
作用:掃描mapper接口注冊到容器中
1)而MapperScannerConfigurer實現了BeanDefinitionRegitryPostProcessor接口,實現了postProcessBeanDefinitionRegistry這個方法
2) 我們進入這個方法scanner.registerFilters();
進入registerFilters
public void registerFilters() { boolean acceptAllInterfaces = true; if(this.annotationClass != null) { this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces = false; } if(this.markerInterface != null) { this.addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { protected boolean matchClassName(String className) { return false; } }); acceptAllInterfaces = false; } if(acceptAllInterfaces) { this.addIncludeFilter((metadataReader, metadataReaderFactory) -> { return true; }); } this.addExcludeFilter((metadataReader, metadataReaderFactory) -> { String className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); }); }
3)對包路徑進行掃描 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
核心是doScan方法。
首先會調用父類的doScan方法:
public Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if(beanDefinitions.isEmpty()) { LOGGER.warn(() -> { return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."; }); } else { this.processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
然后調用this.processBeanDefinitions(beanDefinitions);方法
四、mapper類生成
1、進入processBeanDefinitions方法
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for(Iterator var3 = beanDefinitions.iterator(); var3.hasNext(); definition.setLazyInit(this.lazyInitialization)) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next(); definition = (GenericBeanDefinition)holder.getBeanDefinition(); String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> { return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"; }); definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", Boolean.valueOf(this.addToConfig)); boolean explicitFactoryUsed = false; 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(() -> { return "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(() -> { return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."; }); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if(!explicitFactoryUsed) { LOGGER.debug(() -> { return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."; }); definition.setAutowireMode(2); } } }
里面會將beanClass替換為mapperFactoryBeanClass 。definition.setBeanClass(this.mapperFactoryBeanClass);
2、進入MapperFactoryBean方法
里面有一個getObject方法
public T getObject() throws Exception { return this.getSqlSession().getMapper(this.mapperInterface); }
進入getMapper方法,類型為接口TestMapper
進入getMapper方法
進入getMapper
進入newInstance方法。最終返回一個代理對象。MapperProxy是Mybatis源碼里的內容,這里不做過多的介紹。
五、mapper類執行
1、進入MapperProxy中的invoke方法
進入execute方法
進入增刪改查其中一種類型,然后通過sqlSession進行執行,執行完畢后將結果返回
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }