Spring整合Mybatis有两个大版本,分别是1.3.2版本和2.0.5版本
先看1.3.2版本

一、1.3.2版本

先上代码

  1. @ComponentScan(value = "com.test")
  2. @MapperScan("com.test")
  3. public class AppConfig {
  4. }
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(MapperScannerRegistrar.class)
  5. public @interface MapperScan {
  6. }

通过添加MapperScan注解,导入MapperScannerRegistrar类,下面分析MapperScannerRegistrar类

  1. public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
  2. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  3. AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  4. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  5. // this check is needed in Spring 3.1
  6. if (resourceLoader != null) {
  7. scanner.setResourceLoader(resourceLoader);
  8. }
  9. Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
  10. if (!Annotation.class.equals(annotationClass)) {
  11. scanner.setAnnotationClass(annotationClass);
  12. }
  13. Class<?> markerInterface = annoAttrs.getClass("markerInterface");
  14. if (!Class.class.equals(markerInterface)) {
  15. scanner.setMarkerInterface(markerInterface);
  16. }
  17. Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
  18. if (!BeanNameGenerator.class.equals(generatorClass)) {
  19. scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
  20. }
  21. Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
  22. if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
  23. scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
  24. }
  25. scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
  26. scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
  27. List<String> basePackages = new ArrayList<String>();
  28. for (String pkg : annoAttrs.getStringArray("value")) {
  29. if (StringUtils.hasText(pkg)) {
  30. basePackages.add(pkg);
  31. }
  32. }
  33. for (String pkg : annoAttrs.getStringArray("basePackages")) {
  34. if (StringUtils.hasText(pkg)) {
  35. basePackages.add(pkg);
  36. }
  37. }
  38. for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
  39. basePackages.add(ClassUtils.getPackageName(clazz));
  40. }
  41. scanner.registerFilters();
  42. scanner.doScan(StringUtils.toStringArray(basePackages));
  43. }
  44. }

MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,Spring在启动的时候就会调用MapperScannerRegistrar类的registerBeanDefinitions方法,registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,这个对象会扫描指定包下的mapper接口,然而Spring默认是不扫描接口的,
Mybatis重写了isCandidateComponent方法,代码如下

  1. @Override
  2. protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
  3. return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  4. }

从上面代码可以看出Mybatis只扫描接口。然后通过Spring扫描后,会把扫描出来的接口封装成对应的BeanDefinition,接下来把BeanDefinition进行修改,将BeanClass设置成MapperFactoryBean,把注入模式修改为ByType。

  1. private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  2. GenericBeanDefinition definition;
  3. for (BeanDefinitionHolder holder : beanDefinitions) {
  4. definition = (GenericBeanDefinition) holder.getBeanDefinition();
  5. if (logger.isDebugEnabled()) {
  6. logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
  7. + "' and '" + definition.getBeanClassName() + "' mapperInterface");
  8. }
  9. // the mapper interface is the original class of the bean
  10. // but, the actual class of the bean is MapperFactoryBean
  11. definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
  12. // 将BeanDefinition的BeanClass设置成MapperFactoryBean
  13. definition.setBeanClass(this.mapperFactoryBean.getClass());
  14. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  15. boolean explicitFactoryUsed = false;
  16. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  17. definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  18. explicitFactoryUsed = true;
  19. } else if (this.sqlSessionFactory != null) {
  20. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  21. explicitFactoryUsed = true;
  22. }
  23. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  24. if (explicitFactoryUsed) {
  25. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  26. }
  27. definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  28. explicitFactoryUsed = true;
  29. } else if (this.sqlSessionTemplate != null) {
  30. if (explicitFactoryUsed) {
  31. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  32. }
  33. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  34. explicitFactoryUsed = true;
  35. }
  36. if (!explicitFactoryUsed) {
  37. if (logger.isDebugEnabled()) {
  38. logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
  39. }
  40. // 修改注入模型为ByType
  41. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  42. }
  43. }
  44. }

扫描完成后会,Spring会根据BeanDefinition去创建Bean对象,相当于每个Mapper对应一个BeanFactory,
在MapperBeanFactory的getObject方法中调用了getSqlSession方法去得到一个SqlSession对象,然后根据对应的Mapper接口生成一个代理对象。

  1. @Override
  2. public T getObject() throws Exception {
  3. return getSqlSession().getMapper(this.mapperInterface);
  4. }

sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生,由于上面已经将BeanDefinition的注入模型为ByType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。

二、2.0.5版本

  • 通过@MapperScan导入了MapperScannerRegistrar类
  • MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
  • 在registerBeanDefinitions方法中生成了一个MapperScannerConfigurer类型的BeanDefinition
  • 而MapperScannerConfigurer实现了实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
  • 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描
  • 后续的逻辑和1.3.2版本一样。

带来的好处是,可以不使用@MapperScan注解,而可以直接定义一个Bean,比如:

  1. @Bean
  2. public MapperScannerConfigurer mapperScannerConfigurer() {
  3. MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
  4. mapperScannerConfigurer.setBasePackage("com.test");
  5. return mapperScannerConfigurer;
  6. }