1:在上一篇文章中已经了解了SpringBoot是如何将我们的mapper层接口生成了代理类,所以今天就看下第二部分,SpringBoot如何解析MyBatis的sql的xml文件
2:首先还得从MyBatis的starter开始说起,我们在使用Mybatis的时候需要引入一个MyBatis的starter的依赖,我们可以查看spring.factories文件,然后找到MybatisAutoConfiguration这个类,所以这个类就是我们开始解析的起点。至于为什么??大家可以去看下juejin.cn/post/689443…这篇文章详细记录了如何自定义一个starter,所以我们进入MybatisAutoConfiguration这个类这个类

3:MybatisAutoConfiguration这个类
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
private final LanguageDriver[] languageDrivers;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
4:查看MyBatis的属性源配置类,SpringBoot会去解析以 "mybatis" 为前缀的属性配置信息,当然包括我们需要配置的sql的配置文件路径,也就是mapperLocations
@ConfigurationProperties(
prefix = "mybatis"
)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
private String configLocation;
private String[] mapperLocations;
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
private String typeHandlersPackage;
private boolean checkConfigLocation = false;
private ExecutorType executorType;
private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
private Properties configurationProperties;
@NestedConfigurationProperty
private Configuration configuration;
5:有人会问,MyBatis的确是配置了MybatisAutoConfiguration这个类,那SpringBoot为什么就会解析它呢??
6:解析MybatisAutoConfiguration这个类原理,在SpringBoot启动类上的注解@SpringBootApplication中有一个@EnableAutoConfiguration,在这个注解中有@Import(AutoConfigurationImportSelector.class)如图,相信看过我前面几篇文章的人就会知道对于@Import注解中的类SpringBoot会进行处理,但是它处理的时间与@MapperScan中@Import处理时间不同

7:处理@SpringBootApplication注解中@Import中的类时机
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {}
this.deferredImportSelectorHandler.process();
}

8:执行AutoConfigurationImportSelector类的getAutoConfigurationEntry()方法
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

9:经过过滤之后我们可以发现MyBatisAutoConfiguration这个配置类是符合@Condition条件的,所以SpringBoot会去解析它
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
10 进入 SqlSessionFactoryBean.class,发现它实现了InitializingBean这个接口,实现了这个接口那么就一定会执行afterPropertiesSet()方法,所以我们直接进入到这个方法中去
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
11:执行afterPropertiesSet方法
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
if (this.mapperLocations != null) {
Resource[] var3 = this.mapperLocations;
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
Resource mapperLocation = var3[var5];
if (mapperLocation != null) {
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception var19) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var19);
} finally {
ErrorContext.instance().reset();
}
}
}
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
12:SpringBoot解析MyBatis配置文件到解析sql的xml配置文件大致流程就是这样,大家可以自行debug查看更加细致的流程。MyBatis解析之后就会将我们的sql语句解析成各种形式的SqlNode,然后会封装成一个SqlSource,最终会被分装到一个MapperStatement对象,而当我们执行到mapper层接口的方法之后,就会根据指定的key去找到对应的MapperStatement对象,拿到对应的SqlSource里面的sql语句,并执行。
13:SpringBoot整合MyBatis源码就分析到这里啦,如果文章有错误的话欢迎大家指出,我是个小白,不要喷我写的low哈