springboot整合mybatis源码分析

springboot整合mybatis源码分析

本文主要讲述mybatis在springboot中是如何被加载执行的,由于涉及的内容会比较多,所以这次只会对调用关系及关键代码点进行讲解,为了避免文章太长,读起来昏昏欲睡,一些不影响整体流程的细节就不涉及了。

源码位置https://github.com/wbo112/blogdemo/tree/main/springbootdemo/springboot-mybatis

1、预备知识

  • FactoryBean

    什么是FactoryBean?

    我们先看看FactoryBean的源码

//由 BeanFactory 中使用的对象实现的接口,这些对象本身是单个对象的工厂。如果一个 bean 实现了这个接口,它就被用作一个对象暴露的工厂,而不是直接作为一个将暴露自己的 bean 实例。
//注意:实现此接口的 bean 不能用作普通 bean。 FactoryBean 以 bean 样式定义,但为 bean 引用公开的对象 (getObject()) 始终是它创建的对象。
//FactoryBeans 可以支持单例和原型,并且可以根据需要懒惰地或在启动时急切地创建对象。 SmartFactoryBean 接口允许公开更细粒度的行为元数据。
//该接口在框架本身中被大量使用,例如用于 AOP org.springframework.aop.framework.ProxyFactoryBean 或 org.springframework.jndi.JndiObjectFactoryBean。它也可以用于自定义组件;然而,这仅适用于基础设施代码。
//FactoryBean 是一个程序化契约。实现不应该依赖于注释驱动的注入或其他反射设施。 getObjectType() getObject() 调用可能会在引导过程的早期到达,甚至在任何后处理器设置之前。如果您需要访问其他 bean,请实现 BeanFactoryAware 并以编程方式获取它们。
//容器只负责管理FactoryBean 实例的生命周期,而不负责管理FactoryBean 创建的对象的生命周期。因此,暴露的 bean 对象(例如 java.io.Closeable.close() 上的 destroy 方法不会被自动调用。相反,FactoryBean 应该实现 DisposableBean 并将任何此类关闭调用委托给底层对象。
//最后,FactoryBean 对象参与包含 BeanFactory 的 bean 创建同步。除了 FactoryBean 本身(或类似的)内部的延迟初始化之外,通常不需要内部同步。
package org.springframework.beans.factory; import org.springframework.lang.Nullable; public interface FactoryBean<T> {

String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

//返回真正的beanFacotry中的bean对象

@Nullable<br/>
T getObject() throws Exception;
//返回真正的beanFacotry中的bean对象的类型<br/>
@Nullable<br/>
Class&lt;?&gt; getObjectType();
//是否单例<br/>
default boolean isSingleton() {<br/>
	return true;<br/>
}

}

上面就是FactoryBean的源码了,源码中的注释我都删除掉了。类上的中文注释是翻译的源码上的,方法上的注释是我自己加的。简单来说就是时间这个接口的类是作为对象暴漏的工厂,真正调用getObject()才会得到实际的bean对象。

2、springboot集成mybatis

  • 之前的文章简单说到springboot启动的时候会读取META-INF\spring.factories文件,把key=org.springframework.boot.autoconfigure.EnableAutoConfiguration的字符串作为类名去加载(启动会配合META-INF\spring-autoconfigure-metadata.properties中的内容过滤掉不符合当前场景的)

    springboot集成mybatis也是这样实现的。

  • 是由谁来上面的文件的呢

    我们的main方法上都会有@SpringBootApplication注解

在SpringBootApplication这个上面会有个@EnableAutoConfiguration注解

在这个上面会有import注解,参数是AutoConfigurationImportSelector.class。真正读取上面文件的类就是AutoConfigurationImportSelector。

AutoConfigurationImportSelector.java

//真正的读取代码是在这里

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {<br/>
	if (!isEnabled(annotationMetadata)) {<br/>
		return EMPTY_ENTRY;<br/>
	}<br/>
	AnnotationAttributes attributes = getAttributes(annotationMetadata);<br/>
	//在这里读取META-INF\spring.factories文件中key=org.springframework.boot.autoconfigure.EnableAutoConfiguration的值<br/>
	List&lt;String&gt; configurations = getCandidateConfigurations(annotationMetadata, attributes);<br/>
	configurations = removeDuplicates(configurations);<br/>
	Set&lt;String&gt; exclusions = getExclusions(annotationMetadata, attributes);<br/>
	checkExcludedClasses(configurations, exclusions);<br/>
	configurations.removeAll(exclusions);<br/>
    //在这里读取META-INF\spring.factories文件中key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的值根据META-INF\spring-autoconfigure-metadata.properties进行过滤<br/>
	configurations = getConfigurationClassFilter().filter(configurations);<br/>
	fireAutoConfigurationImportEvents(configurations, exclusions);<br/>
	return new AutoConfigurationEntry(configurations, exclusions);<br/>
}<br/>

读取META-INF\spring-autoconfigure-metadata.properties文件是在AutoConfigurationImportSelector的内部类ConfigurationClassFilter的构造方法中,真正的过滤也是在这个内部类中

		ConfigurationClassFilter(ClassLoader classLoader, List&lt;AutoConfigurationImportFilter&gt; filters) {

	//在这里读取的META-INF\spring-autoconfigure-metadata.properties<br/>
		this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);<br/>
		this.filters = filters;<br/>
	}<br/>

//这个也是ConfigurationClassFilter的方法
List&lt;String&gt; filter(List&lt;String&gt; configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
//执行过滤

  boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);<br/>
  for (int i = 0; i &lt; match.length; i++) {<br/>
     if (!match[i]) {<br/>
        candidates[i] = null;<br/>
        skipped = true;<br/>
     }<br/>
  }<br/>

}

 + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + &#34; ms&#34;);<br/>

}
return result;
}

默认的过滤器是有3个,是在这里

在读取过程中就会读取mybatis-spring-boot-autoconfigure-2.2.0.jar中的META-INF\spring.factories配置(本文第一个图),加载下面两个类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=<br/>
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,<br/>
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

同样的也会用mybatis-spring-boot-autoconfigure-2.2.0.jar中的META-INF\spring-autoconfigure-metadata.properties文件进行过滤。

这里的过滤其实就是用类名+.+Conditional*来作为过滤的

org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnClass=org.apache.ibatis.session.SqlSessionFactory,org.mybatis.spring.SqlSessionFactoryBean
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate=javax.sql.DataSource

比如上面两行org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnClass是根据等号后面的类是否存在来判断是否被过滤掉,org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.ConditionalOnSingleCandidate看代码也是判断对应类是否存在来判断的,多个条件是and的关系

这两个条件的具体判断代码位置在OnBeanCondition中

protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,

  AutoConfigurationMetadata autoConfigurationMetadata) {<br/>

ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i &lt; outcomes.length; i++) {

  String autoConfigurationClass = autoConfigurationClasses[i];<br/>
  if (autoConfigurationClass != null) {<br/>
      //获取*.ConditionalOnClass等号后面的值<br/>
     Set&lt;String&gt; onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, &#34;ConditionalOnBean&#34;);<br/>
      //进行判断,返回null就是OK的,条件不存在也是null<br/>
     outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);<br/>
     if (outcomes[i] == null) {<br/>
         //获取*.ConditionalOnSingleCandidate等号后面的值<br/>
        Set&lt;String&gt; onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,<br/>
              &#34;ConditionalOnSingleCandidate&#34;);<br/>
          //进行判断,返回null就是OK的,条件不存在也是null<br/>
        outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);<br/>
     }<br/>
  }<br/>

}
return outcomes;
}

当前的场景这两个类都是符合的不会被过滤掉。这两个类就会被加载。

3、MybatisAutoConfiguration的加载,beanFatory加载@Mapper类

下面具体看下加载的过程,主要是MybatisAutoConfiguration这个类,所以我们这里也就只看这个类了

//这里就把类上的注解粘了出来简单都介绍下
//这个注解大家都比较熟悉,不多说了
@org.springframework.context.annotation.Configuration
//这个还是条件注解,处理的类和上面配置文件中的处理都在同一个类中
//这个是判断对应类是否存在
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
//这个就和配置文件中的处理有区别了,这个是判断beanFacotry中是否只有一个类型DataSource.class的bean的定义,或者有多个,但有一个主要的
@ConditionalOnSingleCandidate(DataSource.class)
//这个是去让注入配置文件
@EnableConfigurationProperties(MybatisProperties.class)
//这个是排序的
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {

......<br/>

}

下面这个图是ConditionalOnClass,ConditionalOnSingleCandidate执行处理的位置,还是在OnBeanCondition这个类中

在进行完过滤判断后,确定MybatisAutoConfiguration类要加载之后,会扫描内部类和方法上,符合条件的也都会被加载,主要是找@Configuration,@Bean这两个注解。我们当前这个类中依次会加载如下内容

加载这个内部类,ConditionalOnMissingBean这个条件当前是成立的,关于条件这块都会忽略掉,不多说这块了。同时由于类上有Import注解,也就会继续加载AutoConfiguredMapperScannerRegistrar.class这个类,

类上有方法@Bean注解

这两个类也会被加载

这里就会加载MybatisAutoConfiguration,MapperScannerRegistrarNotFoundConfiguration,AutoConfiguredMapperScannerRegistrar,SqlSessionTemplate,SqlSessionFactory这几个作为bean的定义(后面两个是方法)。

由于AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,在加载的过程中,会调用registerBeanDefinitions去注册额外的bean的定义。

这个方法比较重要,我们进去看看

    @Override

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {<br/>
//这个是判断beanFactory中是否存在AutoConfigurationPackages的bean,这里是存在的<br/>
  if (!AutoConfigurationPackages.has(this.beanFactory)) {<br/>
    logger.debug(&#34;Could not determine auto-configuration package, automatic mapper scanning disabled.&#34;);<br/>
    return;<br/>
  }

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

  //这里获取要扫描的包名,这里会是{“com.example.springbootmybatis”},其实也就是我们在哪里找mapper,后面单独说下这个<br/>
  List&lt;String&gt; packages = AutoConfigurationPackages.get(this.beanFactory);<br/>
  if (logger.isDebugEnabled()) {<br/>
    packages.forEach(pkg -&gt; logger.debug(&#34;Using auto-configuration base package &#39;{}&#39;&#34;, pkg));<br/>
  }<br/>
 //下面这些代码主要就是定义一个bean的定义,添加到BeanFactory中<br/>
  BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);<br/>
  builder.addPropertyValue(&#34;processPropertyPlaceHolders&#34;, true);<br/>
  //这就是要扫描的注解类型,就是@Mapper<br/>
  builder.addPropertyValue(&#34;annotationClass&#34;, Mapper.class);<br/>
  //这里是要扫描的包的路径<br/>
  builder.addPropertyValue(&#34;basePackage&#34;, StringUtils.collectionToCommaDelimitedString(packages));<br/>
  BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);<br/>
  Set&lt;String&gt; propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)<br/>
      .collect(Collectors.toSet());<br/>
  if (propertyNames.contains(&#34;lazyInitialization&#34;)) {<br/>
    // Need to mybatis-spring 2.0.2+<br/>
    builder.addPropertyValue(&#34;lazyInitialization&#34;, &#34;${mybatis.lazy-initialization:false}&#34;);<br/>
  }<br/>
  if (propertyNames.contains(&#34;defaultScope&#34;)) {<br/>
    // Need to mybatis-spring 2.0.6+<br/>
    builder.addPropertyValue(&#34;defaultScope&#34;, &#34;${mybatis.mapper-default-scope:}&#34;);<br/>
  }<br/>
  registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());<br/>
}

这里我们来看下AutoConfigurationPackages.get(this.beanFactory)这个获取的包名是如何的

先看下这个方法的里面调用

	public static List&lt;String&gt; get(BeanFactory beanFactory) {

	try {<br/>
        //BEAN = AutoConfigurationPackages.class.getName(),这个方法也就是获取bean的名字是AutoConfigurationPackages.class.getName(),AutoConfigurationPackages.BasePackages.class类型的bean,再调用AutoConfigurationPackages.BasePackages的get方法<br/>
        //下面我们分析下这个值是怎么来的<br/>
		return beanFactory.getBean(BEAN, BasePackages.class).get();<br/>
	}<br/>
	catch (NoSuchBeanDefinitionException ex) {<br/>
		throw new IllegalStateException(&#34;Unable to retrieve @EnableAutoConfiguration base packages&#34;);<br/>
	}<br/>
}<br/>

由于我们main方法的类上有@SpringBootApplication注解,它的注解上有@EnableAutoConfiguration,它的注解上有@AutoConfigurationPackage,它的注解上@Import(AutoConfigurationPackages.Registrar.class),在加载我们的主类SpringbootMybatisApplication时,就会调用到AutoConfigurationPackages.Registrar的registerBeanDefinitions这个方法

	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

	@Override<br/>
    //这里的metadata就是我们的SpringbootMybatisApplication<br/>
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {<br/>
       	//我们先看看new PackageImports(metadata)这个方法<br/>
		register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));<br/>
	}<br/>
......
}<br/>

		//这个比较简单了,就是获取一些包名

	PackageImports(AnnotationMetadata metadata) {
		AnnotationAttributes attributes = AnnotationAttributes<br/>
				.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));<br/>
        //这里是获取我们主类SpringbootMybatisApplication上注解的&#34;basePackages&#34;属性,由于我们没有配置,所以这里就是null<br/>
		List&lt;String&gt; packageNames = new ArrayList&lt;&gt;(Arrays.asList(attributes.getStringArray(&#34;basePackages&#34;)));<br/>
          //这里是获取我们主类SpringbootMybatisApplication上注解的&#34;basePackageClasses&#34;属性,由于我们没有配置,所以也不会走到这个for循环<br/>
		for (Class&lt;?&gt; basePackageClass : attributes.getClassArray(&#34;basePackageClasses&#34;)) {<br/>
			packageNames.add(basePackageClass.getPackage().getName());<br/>
		}<br/>
        //这里的packageNames就是空的,会走到这个if分支<br/>
		if (packageNames.isEmpty()) {<br/>
            //packageNames增加当前SpringbootMybatisApplication类所在的包名com.example.springbootmybatis<br/>
			packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));<br/>
		}<br/>
        //这里的this.packageNames中就只会有com.example.springbootmybatis<br/>
		this.packageNames = Collections.unmodifiableList(packageNames);<br/>
	}<br/>

我们再回头看上面的registerBeanDefinitions

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

@Override<br/>
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {<br/>
   	//上面new PackageImports(metadata)已经分析过了,这时new PackageImports(metadata).getPackageNames().toArray(new String[0])就是{“com.example.springbootmybatis”}<br/>
    //这个方法就不点进去了,在这里简单说说<br/>
    //方法就是在registry(也就是beanFatory)中增加一个bean的定义(BasePackagesBeanDefinition,它的参数就是{“com.example.springbootmybatis”}),所以上面的AutoConfigurationPackages.get(this.beanFactory)这句返回的结果就是{“com.example.springbootmybatis”}<br/>
	register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));<br/>
}<br/>

…… }

我们继续看 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 中在registry(也就是beanFacotry)中增加的bean的定义registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition())

由于MapperScannerConfigurer这个类实现了BeanDefinitionRegistryPostProcessor,所以它就会被生成bean之前加载,调用它的postProcessBeanDefinitionRegistry方法


public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {

if (this.processPropertyPlaceHolders) {<br/>
  //这个是主要设置一些属性,比如上面包名,要扫描的注解类名称等等<br/>
  processPropertyPlaceHolders();<br/>
}<br/>
//这个类看名字,大家都知道是干什么的了。主要就是扫描mapper注解的类<br/>
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);<br/>
scanner.setAddToConfig(this.addToConfig);<br/>
scanner.setAnnotationClass(this.annotationClass);<br/>
scanner.setMarkerInterface(this.markerInterface);<br/>
scanner.setSqlSessionFactory(this.sqlSessionFactory);<br/>
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);<br/>
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);<br/>
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);<br/>
scanner.setResourceLoader(this.applicationContext);<br/>
scanner.setBeanNameGenerator(this.nameGenerator);<br/>
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);<br/>
if (StringUtils.hasText(lazyInitialization)) {<br/>
  scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));<br/>
}<br/>
if (StringUtils.hasText(defaultScope)) {<br/>
  scanner.setDefaultScope(defaultScope);<br/>
}<br/>
//这里是设置要扫描的注解类,这里会设置@Mapper<br/>
scanner.registerFilters();<br/>
//这里就是要根据传入的包名去做扫描了,这里的this.basePackage就是上面说的com.example.springbootmybatis<br/>
scanner.scan(<br/>
    StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));<br/>

}

	public int scan(String… basePackages) {

    ......<br/>
	//在这里进行mapper的扫描<br/>
	doScan(basePackages);<br/>
	......<br/>
}

@Override
public Set&lt;BeanDefinitionHolder&gt; doScan(String… basePackages) {

//首先会进入这里,我们进去看看<br/>

Set&lt;BeanDefinitionHolder&gt; beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) {

LOGGER.warn(() -&gt; &#34;No MyBatis mapper was found in &#39;&#34; + Arrays.toString(basePackages)<br/>
    + &#34;&#39; package. Please check your configuration.&#34;);<br/>

} else {

 //我们会走到这里,这个方法也比较重要,我们进去看看<br/>
processBeanDefinitions(beanDefinitions);<br/>

} return beanDefinitions;
}

	protected Set&lt;BeanDefinitionHolder&gt; doScan(String… basePackages) {

	Assert.notEmpty(basePackages, &#34;At least one base package must be specified&#34;);<br/>
	Set&lt;BeanDefinitionHolder&gt; beanDefinitions = new LinkedHashSet&lt;&gt;();<br/>
    //根据传入的包名遍历<br/>
	for (String basePackage : basePackages) {<br/>
        //这里就是扫描类路径下的mapper注解类了。<br/>
        //比如我这里的传入的包名是com.example.springbootmybatis,就会被转换成classpath*:com/example/springbootmybatis/**/*.class这个路径进行解析查找,将找到的类作为BeanDefinition的定义,返回<br/>
		Set&lt;BeanDefinition&gt; candidates = findCandidateComponents(basePackage);<br/>
		for (BeanDefinition candidate : candidates) {<br/>
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);<br/>
			candidate.setScope(scopeMetadata.getScopeName());<br/>
            //获取bean的名字<br/>
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);<br/>
            //这里candidate的类型是ScannedGenericBeanDefinition,所以会进入这个if分支,这个没啥,就是设置一些bean初始化相关属性,不关注了<br/>
			if (candidate instanceof AbstractBeanDefinition) {<br/>
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);<br/>
			}<br/>
            //也会进入这个if分支,这个也不进去看了<br/>
			if (candidate instanceof AnnotatedBeanDefinition) {<br/>
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);<br/>
			}<br/>
            //这个是判断beanFactory是否包含beanName的bean的定义,不包含就会进入分支,这个分支也没啥特殊的,就是把bean的定义添加到beanFactory中<br/>
			if (checkCandidate(beanName, candidate)) {<br/>
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);<br/>
				definitionHolder =<br/>
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);<br/>
				beanDefinitions.add(definitionHolder);<br/>
				registerBeanDefinition(definitionHolder, this.registry);<br/>
			}<br/>
		}<br/>
	}<br/>
	return beanDefinitions;<br/>
}<br/>

继续会进入这个方法,这个方法比较长,不过比较重要,大家一起跟我看吧,非关键代码我都省略掉吧

private void processBeanDefinitions(Set&lt;BeanDefinitionHolder&gt; beanDefinitions) {

......<br/>
//这里是给bean定义的添加一个构造方法参数,就是我们扫描出来mapper注解类的类名,我这里是com.example.springbootmybatis.mapper.UserMapper。这个是为后续选择哪个构造方法服务的<br/>
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59<br/>
//这个就是设置对应bean的类的类,这里设置成了org.mybatis.spring.mapper.MapperFactoryBean这个类,这注意这个类实现了FactoryBean接口<br/>
definition.setBeanClass(this.mapperFactoryBeanClass);<br/>
......

if (!explicitFactoryUsed) {

  LOGGER.debug(() -&gt; &#34;Enabling autowire by type for MapperFactoryBean with name &#39;&#34; + holder.getBeanName() + &#34;&#39;.&#34;);<br/>
   //这句也比较重要,代表属性注入模式<br/>
  definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);<br/>
}<br/>
......<br/>

}
}

到这里,对于@Mapper类的加载就完成了,后面的都是在生成对应bean的时候完成的

4.beanFatory生成对应@Mapper类的bean对象

创建bean对象实例会调用到AbstractBeanFactory的doGetBean这个方法

protected &lt;T&gt; T doGetBean(

		String name, @Nullable Class&lt;T&gt; requiredType, @Nullable Object[] args, boolean typeCheckOnly)<br/>
		throws BeansException {<br/>
			......<br/>
			// Create bean instance.<br/>
            //由于我们的是单例对象,会走到这个分支<br/>
			if (mbd.isSingleton()) {<br/>
				sharedInstance = getSingleton(beanName, () -&gt; {<br/>
					try {<br/>
                        //在这个方法中会创建bean对象,我们下面看看这个方法<br/>
						return createBean(beanName, mbd, args);<br/>
					}<br/>
					catch (BeansException ex) {<br/>
						// Explicitly remove instance from singleton cache: It might have been put there<br/>
						// eagerly by the creation process, to allow for circular reference resolution.<br/>
						// Also remove any beans that received a temporary reference to the bean.<br/>
						destroySingleton(beanName);<br/>
						throw ex;<br/>
					}<br/>
				});<br/>
                //由于我们的sharedInstance对象是,所以在这里最终会调用到FactoryBeanRegistrySupport的doGetObjectFromFactoryBean方法,返回真正的userMapper的bean对象,也就是调用MapperFactoryBean的getObject()方法<br/>
				beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);<br/>
			}<br/>
}

创建bean对象实例最终都会走到AbstractAutowireCapableBeanFactory类的doCreateBean这个方法

	//我们来分析下这块代码,不相关的代码我都省略掉

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)<br/>
		throws BeanCreationException {
	// Instantiate the bean.<br/>
	BeanWrapper instanceWrapper = null;<br/>
	if (mbd.isSingleton()) {<br/>
        //看this.factoryBeanInstanceCache这个名字就知道是factoryBean实例的缓存,其实我们当前的userMapper创建的实例已经缓存到这里了,不过无所谓,就算之前没有创建缓存到这里,下面12行就会去创建。所以我们这里就认为之前没有创建过,去看看13行的代码具体是如何创建userMapper的factoryBean实例的<br/>
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);<br/>
	}<br/>
	if (instanceWrapper == null) {<br/>
        //正常创建userMapper的factoryBean对象是走不到这里的,是在这之前创建的,不过创建方法也是调用的这个<br/>
        //这个方法的所用就是根据RootBeanDefinition的getBeanClass()找到对应的类,再查找所有构造方法,根据 RootBeanDefinition.getConstructorArgumentValues()构造方法的参数选择合适的构造方法创建类对象,返回BeanWrapperImpl包装的对象<br/>
        //上面的processBeanDefinitions的方法中对RootBeanDefinition的beanClass和constructorArgumentValues都做过了专门的设置。<br/>
        //所以我们这里其实是调用的MapperFactoryBean(Class&lt;T&gt; mapperInterface)这个构造方法,里面的参数mapperInterface就是我们的mapper类com.example.springbootmybatis.mapper.UserMapper<br/>
		instanceWrapper = createBeanInstance(beanName, mbd, args);<br/>
	}<br/>
    //这个就是获取创建出来的MapperFactoryBean对象<br/>
	Object bean = instanceWrapper.getWrappedInstance();<br/>
    //这个是创建创建出来的对象的类型,也就是org.mybatis.spring.mapper.MapperFactoryBean<br/>
	Class&lt;?&gt; beanType = instanceWrapper.getWrappedClass();<br/>
	if (beanType != NullBean.class) {<br/>
		mbd.resolvedTargetType = beanType;<br/>
	}<br/>
	......
	// Initialize the bean instance.<br/>
	Object exposedObject = bean;<br/>
	try {<br/>
        //这里就是属性填充了,我们去这里看看<br/>
		populateBean(beanName, mbd, instanceWrapper);<br/>
        //由于我们的MapperFactoryBean继承了SqlSessionDaoSupport,它继承了DaoSupport,它实现了InitializingBean这个接口,所以在这里也会调用到DaoSupport的afterPropertiesSet方法<br/>
        exposedObject = initializeBean(beanName, exposedObject, mbd);<br/>
    }<br/>
	return exposedObject;<br/>
}<br/>

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

   ......<br/>
       //这个是获取RootBeanDefinition属性注入模式,我们的是在上面processBeanDefinitions这个方法中设置过的<br/>
       //definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);就是这句<br/>
	int resolvedAutowireMode = mbd.getResolvedAutowireMode();<br/>
	if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {<br/>
		......<br/>
		if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {<br/>
            //最终会走到这里,遍历类的所有属性,使用unsatisfiedNonSimpleProperties方法进行过滤,对属性进行注入。我们这里会对sqlSessionFactory,sqlSessionTemplate这两个属性进行注入(这两个注入的属性都是在MybatisAutoConfiguration的类中,通过方法定义的bean对象,上面也说过了)<br/>
			autowireByType(beanName, mbd, bw, newPvs);<br/>
		}<br/>
		pvs = newPvs;<br/>
	}<br/>
	......<br/>
}

DaoSupport的afterPropertiesSet方法


public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {<br/>
	// Let abstract subclasses check their configuration.<br/>
    //这个方法是由子类MapperFactoryBean实现的,我们进去看看<br/>
	checkDaoConfig();
	// Let concrete implementations initialize themselves.<br/>
	try {<br/>
		initDao();<br/>
	}<br/>
	catch (Exception ex) {<br/>
		throw new BeanInitializationException(&#34;Initialization of DAO failed&#34;, ex);<br/>
	}<br/>
}<br/>

MapperFactoryBean的checkDaoConfig方法

  protected void checkDaoConfig() {

......<br/>
//这个是获取之前注入的SqlSessionTemplate的Configuration<br/>
Configuration configuration = getSqlSession().getConfiguration();<br/>
if (this.addToConfig &amp;&amp; !configuration.hasMapper(this.mapperInterface)) {<br/>
  try {<br/>
     //会在这里添加我们的mapper类,这里的this.mapperInterface就是com.example.springbootmybatis.mapper.UserMapper,我们进到这里去看看<br/>
    configuration.addMapper(this.mapperInterface);<br/>
  } catch (Exception e) {<br/>
    logger.error(&#34;Error while adding the mapper &#39;&#34; + this.mapperInterface + &#34;&#39; to configuration.&#34;, e);<br/>
    throw new IllegalArgumentException(e);<br/>
  } finally {<br/>
    ErrorContext.instance().reset();<br/>
  }<br/>
}<br/>

}

Configuration的addMapper方法

  public &lt;T&gt; T getMapper(Class&lt;T&gt; type, SqlSession sqlSession) {

return mapperRegistry.getMapper(type, sqlSession);<br/>

}

MapperRegistry的addMapper方法

  public &lt;T&gt; void addMapper(Class&lt;T&gt; type) {

	......<br/>
  try {<br/>
    //在knownMappers中添加一个key=com.example.springbootmybatis.mapper.UserMapper的MapperProxyFactory对象<br/>
    knownMappers.put(type, new MapperProxyFactory&lt;&gt;(type));<br/>
    // It&#39;s important that the type is added before the parser is run<br/>
    // otherwise the binding may automatically be attempted by the<br/>
    // mapper parser. If the type is already known, it won&#39;t try.<br/>
    //在这里就是去查找mapper.xml文件了,同样的如果我们不是通过xml配置的sql,而是用注解的方式实现的,具体的查找都是通过下面的parse方法来实现,我们进去parse方法看看<br/>
    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);<br/>
    parser.parse();<br/>
    loadCompleted = true;<br/>
  } finally {<br/>
    if (!loadCompleted) {<br/>
      knownMappers.remove(type);<br/>
    }<br/>
  }<br/>
}<br/>

}

MapperAnnotationBuilder的parse方法

  public void parse() {

String resource = type.toString();<br/>
if (!configuration.isResourceLoaded(resource)) {<br/>
  //这行就是去从类路径加载mapper的xml文件了,具体的路径规则是这样的type.getName().replace(&#39;.&#39;, &#39;/&#39;) + &#34;.xml&#34;。所以如果我们的mapper的xml文件是按照这种规则指定的,就不需要单独通过mybatis.mapper-locations去单独指定mapper.xml的路径了<br/>
  loadXmlResource();<br/>
  configuration.addLoadedResource(resource);<br/>
  assistant.setCurrentNamespace(type.getName());<br/>
  parseCache();<br/>
  parseCacheRef();<br/>
  //下面这块就是去扫描方法上的注解去生成sql配置了,这里就不进去看了<br/>
  for (Method method : type.getMethods()) {<br/>
    if (!canHaveStatement(method)) {<br/>
      continue;<br/>
    }<br/>
    if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()<br/>
        &amp;&amp; method.getAnnotation(ResultMap.class) == null) {<br/>
      parseResultMap(method);<br/>
    }<br/>
    try {<br/>
      parseStatement(method);<br/>
    } catch (IncompleteElementException e) {<br/>
      configuration.addIncompleteMethod(new MethodResolver(this, method));<br/>
    }<br/>
  }<br/>
}<br/>
parsePendingMethods();<br/>

}

我们再回到doGetBean方法看后面的

protected &lt;T&gt; T doGetBean(

		String name, @Nullable Class&lt;T&gt; requiredType, @Nullable Object[] args, boolean typeCheckOnly)<br/>
		throws BeansException {<br/>
				......<br/>
                //由于我们的sharedInstance对象是,所以在这里最终会调用到FactoryBeanRegistrySupport的doGetObjectFromFactoryBean方法,返回真正的userMapper的bean对象,也就是调用MapperFactoryBean的getObject()方法<br/>
				beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);<br/>
			}<br/>
}<br/>

MapperFactoryBean的getObject方法

  public T getObject() throws Exception {
//这个getSqlSession()就是我们上面属性注入的。org.mybatis.spring.SqlSessionTemplate的对象,

return getSqlSession().getMapper(this.mapperInterface);<br/>

}

最终会调用到MapperRegistry的getMapper方法


public &lt;T&gt; T getMapper(Class&lt;T&gt; type, SqlSession sqlSession) {<br/>
// 我们在上面addMapper的方法中讲过knownMappers已经添加了key=com.example.springbootmybatis.mapper.UserMapper的MapperProxyFactory对象,<br/>
final MapperProxyFactory&lt;T&gt; mapperProxyFactory = (MapperProxyFactory&lt;T&gt;) knownMappers.get(type);<br/>
if (mapperProxyFactory == null) {<br/>
  throw new BindingException(&#34;Type &#34; + type + &#34; is not known to the MapperRegistry.&#34;);<br/>
}<br/>
try {<br/>
  //这里就是调用MapperProxyFactory的newInstance的方法了<br/>
  return mapperProxyFactory.newInstance(sqlSession);<br/>
} catch (Exception e) {<br/>
  throw new BindingException(&#34;Error getting mapper instance. Cause: &#34; + e, e);<br/>
}<br/>

}

MapperProxyFactory的newInstance方法

public T newInstance(SqlSession sqlSession) {
final MapperProxy&lt;T&gt; mapperProxy = new MapperProxy&lt;&gt;(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy&lt;T&gt; mapperProxy) {
//最终调用到这里,创建一个MapperProxy的代理对象,这个也就是真正的创建的bean对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

到这里,springboot整合mybatis到创建出来mapper对象,整个流程就到这里了,后面调用mapper的方法其实也就是通过MapperProxy代理来实现的。具体springboot中调用mybatis的执行的流程将在接下来的一篇给大家讲解。

5.关于@MapperScan

@MapperScan(basePackages = “com.example.springbootmybatis”,annotationClass = Mapper.class)
public void registerFilters() {
boolean acceptAllInterfaces = true; // if specified, use the given annotation and / or marker interface
//如果设置了annotationClass = Mapper.class就会走到这里,在这里会过滤class上是否有Mapper注解
if (this.annotationClass != null) {

 addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));<br/>
 acceptAllInterfaces = false;<br/>

} // override AssignableTypeFilter to ignore matches on the actual marker interface
if (this.markerInterface != null) {

 addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {<br/>
   @Override<br/>
   protected boolean matchClassName(String className) {<br/>
     return false;<br/>
   }<br/>
 });<br/>
 acceptAllInterfaces = false;<br/>

} //没有设置了annotationClass = Mapper.class,就会走这里,直接返回true
if (acceptAllInterfaces) {

 // default include filter that accepts all classes<br/>
 addIncludeFilter((metadataReader, metadataReaderFactory) -&gt; true);<br/>

} // exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -&gt; {

 String className = metadataReader.getClassMetadata().getClassName();<br/>
 return className.endsWith(&#34;package-info&#34;);<br/>

});
}

大家看到我的demo中是没有使用@MapperScan这个注解的,那什么时候使用这个注解呢,下面我们从源码来看看

MapperScan注解上面会有@Import(MapperScannerRegistrar.class),@Repeatable(MapperScans.class)这两个注解,MapperScannerRegistrar这个注解实现了ImportBeanDefinitionRegistrar,加载主类的过程中会调用registerBeanDefinitions这个方法,

  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

//这个是获取主类上MapperScan注解的相关属性比如我们的配置是(@MapperScan(basePackages = &#34;com.example.springbootmybatis&#34;)),比如basePackages属性等等都是这个注解上的<br/>
AnnotationAttributes mapperScanAttrs = AnnotationAttributes<br/>
    .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));<br/>
if (mapperScanAttrs != null) {<br/>
  //这里就会根据这些属性创建一个MapperScannerConfigurer类的bean的定义,添加到beanFatory中<br/>
  registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,<br/>
      generateBaseBeanName(importingClassMetadata, 0));<br/>
}<br/>

}

我们再看看之前说的MybatisAutoConfiguration.MapperScannerRegistrarNotFoundConfiguration这个内部类

  @org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
//由于我们上面已经在beanFactory中添加了MapperScannerConfigurer这个类型的bean的定义,所以这个条件就不会成立,上面的import注解中导入AutoConfiguredMapperScannerRegistrar类也就不会执行
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { @Override

public void afterPropertiesSet() {<br/>
  logger.debug(<br/>
      &#34;Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.&#34;);<br/>
}

}

所以@MapperScan的区别主要就在于MapperScannerConfigurer这个bean定义的生成位置不一样

整个的内容比较多,如果大家觉的哪里讲的不清晰或不清楚的,欢迎评论区留言。