SpringBoot整合MyBatis源码分析(一)

448 阅读7分钟

1:SpringBoot整合Mybatis这一块有点多,所以我打算分几部分来记录,整个流程大致是这样的,首先我们会配置一下我们编写sql的xml文件的位置,然后会编写dao层,最后在启动类上加上@MapperScan注解

2:Spring会解析我们配置的dao层接口,但是实例化出来的Bean都是代理类,然后会解析sql的xml配置文件,最后在执行dao层方法的时候通过动态代理会执行MapperProxy类的invoke()方法,最终返回数据库结果,所以今天先记录下SpringBoot是如何解析dao层接口,并将其生成代理类

3:创建一个SpringBoot项目,编写配置文件,整体结构图如下,还是很简单的

4:我们并没有在StudentMapper对象上加任何Spring的相关注解,那么Spring是如何将这个包下的对象都注册进容器的呢??那肯定是加了@MapperScan注解了,我们点进这个注解看下,发现有一个@Import注解,这个注解的作用是SpringBoot在初始化bean的时候也会处理这个注解中的bean,如果不清楚SpringBoot是如何初始化Bean的,可以看我主页的相关文章。

4:在ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        //上面省略若干行
        do {
            //parse方法就是会处理@Import注解中的类,也会去注册Bean,具体的在另一篇文章解析过了
            parser.parse(candidates);
            parser.validate();

            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);

            //重点关注这个方法
            this.reader.loadBeanDefinitions(configClasses);
           
        }

5:进入this.reader.loadBeanDefinitions(configClasses);这个方法

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
         	//进入这个方法之后,继续跟进这个方法
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}


private void loadBeanDefinitionsForConfigurationClass(
            ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

        //重点关注这个方法,我们可以跟进这个方法,然后debug看下,其中的一个就是@MapperScan注解中的@Import注解中的那个类
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

6:执行方法

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
        registrars.forEach((registrar, metadata) ->
                //会去执行MapperScanRegister类的registerBeanDefinitions方法
                registrar.registerBeanDefinitions(metadata, this.registry));
    }
    

7:进入MapperScanRegister.class的registerBeanDefinitions方法,其实看这个方法名字就可以知道这是往Spring容器中注册一个BeanDefinition了。整个方法就是为这个Bean注册一些属性,但是有几个属性要特别注意一下

void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        
        //1:这个属性设置为了true,需要记住,后面会用到
        builder.addPropertyValue("processPropertyPlaceHolders", true);
        
        //中间的一些属性设置我忽略了
        
        //下面这些属性就是设置我们dao层接口类的位置,可以看下图
        List<String> basePackages = new ArrayList();
        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
        String lazyInitialization = annoAttrs.getString("lazyInitialization");
        if (StringUtils.hasText(lazyInitialization)) {
            builder.addPropertyValue("lazyInitialization", lazyInitialization);
        }

        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

 }

8:在第7步我们设置好了我们mapper接口所在的包路径,然后就会去注册一个Bean了,重点来了!!!!,注册的这个Bean是什么??从图中可以看到是MapperScannerConfigure,记住不是MapperScannerResistar这个类,不要搞错了

9:MapperScannerConfigure.class类的结构,它实现了BeanDefinitionRegistryPostProcessor这个接口,这个接口的定义是:在注册bean之后会调用它的后置处理器也就是:void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;这个方法

10:进入MapperScannerConfigure.class类的postProcessBeanDefinitionRegistry方法

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        //这个属性在第7步说到过的要记住的,因为设置为了true,所以会进入if中的方法
        if (this.processPropertyPlaceHolders) {
            //你可以进入这个方法看下,其实就是给一些变量赋值,那么这些值是从哪来的呢??
            //还是在第7步,我们当初注册的就是MapperScannerConfigure这个Bean,而且我们还
            //给它设置了很多属性,这些属性就是从那里获取到的
            this.processPropertyPlaceHolders();
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(this.lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
        }

        scanner.registerFilters();
        //核心方法:进入这个方法
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
    
    //进入到这个方法之后,还是关注核心方法
    publicublic int scan(String... basePackages) {
	int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    	//但是这个方法有二种实现了,一种是Spring的实现,一种是MyBatis的实现,所以我们要进入到
        //MyBatis包下的ClassPathMapperScanner类的doScan()方法
	doScan(basePackages);

	// Register annotation config processors, if necessary.
	if (this.includeAnnotationConfig) {
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

	return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

11:进入到ClassPathMapperScanner类的doScan()方法

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //这个方法就是获取我们mapper接口对象的beanDefinition,可以看下图,因为暂时只有一个接口,所以Set集合中只有一个元素
        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 {
            //获取到所有的BeanDefinition之后会做进一步的处理
            this.processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

12:进入this.processBeanDefinitions(beanDefinitions);方法,其实这个方法就是MyBatis会mapper层接口生成代理类的关键了,可以看到处理之后我们的BeanDefinition编号才能了MapperFactoryBean。这个到底有什么用呢??我们只需要记住一点,等真正实例化Bean的时候,也就是在AbstractBeanFactory的doGetBean方法中,只要是实例化mapper下的接口返回的都是MapperFactoryBean,因为Spring在实例化bean都是先获取Bean对用的BeanDefinition之后,根据BeanDefinition进行实例化Bean。扯远了哈,,,,,所以你只需要记住这个方法的作用就是改造你的mapper接口下的Bean。然后注册进Spring容器中

13:到这里Mapper接口的Bean就已经注册好了,这也是为什么我们明明没有在mapper接口上加任何有关Spring的注解,但是Spring还是会将这些Bean注册进容器的原因。但是虽然Bean是注册了,但是还没有结束,因为我们的代理类还没有生成好。

14:生成代理类是在实例化Bean的时候,也就是在AbstractBeanFactory的doGetBean()方法中,这是实例化Bean的方法,可以看到我们其实是想实例化StudentMapper这个Bean的,但是实例化之后确实MapperFactoryBean这个对象,至于为什么可以看第12步

if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    //生成代理类的核心方法
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

15:进入bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

  protected Object getObjectForBeanInstance(
              Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

          // Don't let calling code try to dereference the factory if the bean isn't a factory.
          if (BeanFactoryUtils.isFactoryDereference(name)) {
              if (beanInstance instanceof NullBean) {
                  return beanInstance;
              }
              if (!(beanInstance instanceof FactoryBean)) {
                  throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
              }
          }

          // Now we have the bean instance, which may be a normal bean or a FactoryBean.
          // If it's a FactoryBean, we use it to create a bean instance, unless the
          // caller actually wants a reference to the factory.
          if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
              return beanInstance;
          }

          Object object = null;
          if (mbd == null) {
              object = getCachedObjectForFactoryBean(beanName);
          }
          if (object == null) {
              // Return bean instance from factory.
              FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
              // Caches object obtained from FactoryBean if it is a singleton.
              if (mbd == null && containsBeanDefinition(beanName)) {
                  mbd = getMergedLocalBeanDefinition(beanName);
              }
              boolean synthetic = (mbd != null && mbd.isSynthetic());
              //最终会进入到这个方法,因为MapperFactoryBean是实现了factoryBean这个接口的
              object = getObjectFromFactoryBean(factory, beanName, !synthetic);
          }
          return object;
      }
      

      protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
	if (factory.isSingleton() && containsSingleton(beanName)) {
		synchronized (getSingletonMutex()) {
			Object object = this.factoryBeanObjectCache.get(beanName);
			if (object == null) {
				object = doGetObjectFromFactoryBean(factory, beanName);
				// Only post-process and store if not put there already during getObject() call above
				// (e.g. because of circular reference processing triggered by custom getBean calls)
				Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
				if (alreadyThere != null) {
					object = alreadyThere;
				}
				else {
					if (shouldPostProcess) {
						if (isSingletonCurrentlyInCreation(beanName)) {
							// Temporarily return non-post-processed object, not storing it yet..
							return object;
						}
						beforeSingletonCreation(beanName);
						try {
							object = postProcessObjectFromFactoryBean(object, beanName);
						}
						catch (Throwable ex) {
							throw new BeanCreationException(beanName,
									"Post-processing of FactoryBean's singleton object failed", ex);
						}
						finally {
							afterSingletonCreation(beanName);
						}
					}
					if (containsSingleton(beanName)) {
						this.factoryBeanObjectCache.put(beanName, object);
					}
				}
			}
			return object;
		}
	}
	else {
		Object object = doGetObjectFromFactoryBean(factory, beanName);
		if (shouldPostProcess) {
			try {
				object = postProcessObjectFromFactoryBean(object, beanName);
			}
			catch (Throwable ex) {
				throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
			}
		}
		return object;
	}
}



private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
		throws BeanCreationException {

	Object object;
	try {
		if (System.getSecurityManager() != null) {
			AccessControlContext acc = getAccessControlContext();
			try {
				object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
			}
			catch (PrivilegedActionException pae) {
				throw pae.getException();
			}
		}
		else {
            		//最终会执行这个方法,因为studentMapper是MapperFactoryBean,所以进入到MapperFactoryBean中的getObject()方法
			object = factory.getObject();
		}
	}
	catch (FactoryBeanNotInitializedException ex) {
		throw new BeanCurrentlyInCreationException(beanName, ex.toString());
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
	}

	// Do not accept a null value for a FactoryBean that's not fully
	// initialized yet: Many FactoryBeans just return null then.
	if (object == null) {
		if (isSingletonCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(
					beanName, "FactoryBean which is currently in creation returned null from getObject");
		}
		object = new NullBean();
	}
	return object;
}

16 进入MapperFactoryBean的getObject()方法中

 public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }
    
    
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                //最终通过代理工厂返回一个代理类
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
}

17:以上就是SpringBoot为我们MyBatis的Mapper接口生成代理类的底层逻辑