Mybatis-Spring原理分析 -- @MapperScacn(Spring Boot中mapper层是如何初始化并注册到Spring容器的)

1,459 阅读5分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

原理说明

作用

根据@MapperScan注解配置的包路径,扫描所有mapper接口,创建BeanDefinition对象,修改beanClass属性值为MapperFactoryBean,注册到Spring容器中,为后续Bean初始化做准备。

流程

  1. @MapperScan注解通过@Import方法导入MapperScannerRegistrar类,MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,覆写了registerBeanDefinitions方法,作用为手动注册某个Bean的BeanDefinition到容器中【DefaultListableBeanFactory=>beanDefinitionMap】。
  2. ImportBeanDefinitionRegistrar接口是Spring的扩展点之一,Spring容器启动时会回调所有实现了ImportBeanDefinitionRegistrar接口的实现类中的registerBeanDefinitions方法,完成自定义BeanDefinition注册。
  3. MapperScannerRegistrar的registerBeanDefinitions方法手动将MapperScannerConfigurer类通过这种方式将自己的BeanDefinition实例注册到了容器中,为下一步Bean初始化做准备。
  4. MapperScannerConfigurer类实现了BeanDefinitionRegistryPostProcessor接口,该接口也是Spring的扩展点之一,Spring容器启动时会回调所有实现了BeanDefinitionRegistryPostProcessor接口的实现类中的postProcessBeanDefinitionRegistry方法,进行BeanDefinition注册的后置处理,可以修改BeanDefinition对象。
  5. 在MapperScannerConfigurer的postProcessBeanDefinitionRegistry方法中创建了ClassPathMapperScanner对象,该对象对@MapperScan注解中配置的包路径进行了扫描,为每个mapper接口创建对应的BeanDefinition实例,并修改所有实例中的beanClass属性值为MapperFactoryBean,autoWireMode为byType。
  6. 将所有mapper接口的BeanDefinition实例注册到Spring的容器中,为下一步实例化mapper接口做准备。
  7. MapperFactoryBean是一个很关键的类,MapperFactryBean集成了SqlSessionDaoSupport类,实现了FactoryBean接口,覆写了getObject()方法。
  8. FactoryBean类型的Bean,在进行Bean初始化时,会通过调用自己的getObject()方法,获取对象;而MapperFactoryBean覆写后的getObject()方法,实际执行的是getSqlSession().getMapper(this.mapperInterface),通过此方法衔接到Mybaits,以JDK动态代理的方式,创建了一个代理对象,最后将代理对象注册到了Spring容器中

时序图

在这里插入图片描述

在这里插入图片描述

Spring扩展点ImportBeanDefinitionRegistrar

public interface ImportBeanDefinitionRegistrar {

   /**
    * Register bean definitions as necessary based on the given annotation metadata of
    * the importing {@code @Configuration} class.
    * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
    * registered here, due to lifecycle constraints related to {@code @Configuration}
    * class processing.
    * @param importingClassMetadata annotation metadata of the importing class
    * @param registry current bean definition registry
    */
    // 通过@Import的方式注册Bean定义
   public void registerBeanDefinitions(
         AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

Spring扩展点BeanDefinitionRegistryPostProcessor

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   /**
    * Modify the application context's internal bean definition registry after its
    * standard initialization. All regular bean definitions will have been loaded,
    * but no beans will have been instantiated yet. This allows for adding further
    * bean definitions before the next post-processing phase kicks in.
    * @param registry the bean definition registry used by the application context
    * @throws org.springframework.beans.BeansException in case of errors
    */
    // Bean定义注册完毕之后,进行后置处理
   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

注解@MapperScacn定义

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
  String[] value() default {};
  String[] basePackages() default {};
  Class<?>[] basePackageClasses() default {};
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  Class<? extends Annotation> annotationClass() default Annotation.class;
  Class<?> markerInterface() default Class.class;
  String sqlSessionTemplateRef() default "";
  String sqlSessionFactoryRef() default "";
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
  String lazyInitialization() default "";
}
  1. 通过@Import注解,导入了MapperScannerRegistrar类
  2. value、basePackages作用一致,用于声明待扫描的mapper层包路径,支持多组。
  3. basePackageClasses用于指定扫描某个类所在包下的所有组件。(@SpringBootApplication标识的启动类)
  4. factoryBean:指定FactoryBean实现类,用于生成接口代理类,默认为MapperFactoryBean.class, 支持自定义。

MapperScannerRegistrar相关

该类实现了ImportBeanDefinitionRegistrar接口,在启动时回调registerBeanDefinitions方法注册MapperScannerConfigurer.class的BeanDefinition到容器中。

圖片

关于ImportBeanDefinitionRegistrar:Spring扩展点之一,启动时会回调被覆盖的registerBeanDefinitions方法,注册BeanDefinition到容器。

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  AnnotationAttributes mapperScanAttrs = AnnotationAttributes
      .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
  if (mapperScanAttrs != null) {
    registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
  }
}

void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
  // 建造者模式创建MapperScannerConfigurer  Bean定义对象
  BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
  builder.addPropertyValue("processPropertyPlaceHolders", true);
  Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
  if (!Annotation.class.equals(annotationClass)) {
    builder.addPropertyValue("annotationClass", annotationClass);
  }
  Class<?> markerInterface = annoAttrs.getClass("markerInterface");
  if (!Class.class.equals(markerInterface)) {
    builder.addPropertyValue("markerInterface", markerInterface);
  }
  Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
  if (!BeanNameGenerator.class.equals(generatorClass)) {
    builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
  }
  Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
  if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
    builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
  }
  String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
  if (StringUtils.hasText(sqlSessionTemplateRef)) {
    builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
  }
  String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
  if (StringUtils.hasText(sqlSessionFactoryRef)) {
    builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
  }
  List<String> basePackages = new ArrayList<>();
  basePackages.addAll(
      Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
  basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
      .collect(Collectors.toList()));
  basePackages.addAll(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());
}

MapperScannerConfigurer相关

该类实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor也是Spring的扩展点之一,启动时回调被覆盖的postProcessBeanDefinitionRegistry方法。在回调方法中创建了ClassPathMapperScanner对象,并调用doScan(basePackages)方法对@MapperScacn中的包路径进行扫描创建、并修改BeanDefinition。 在这里插入图片描述

ClassPathMapperScanner相关

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);
}

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  if (beanDefinitions.isEmpty()) {
    LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
        + "' package. Please check your configuration.");
  } else {
    processBeanDefinitions(beanDefinitions);
  }
  return beanDefinitions;
}

扫描mapper层所有接口的Bean定义,设置beanClass和autoWireMode。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  GenericBeanDefinition definition;
  for (BeanDefinitionHolder holder : beanDefinitions) {
    definition = (GenericBeanDefinition) holder.getBeanDefinition();
    String beanClassName = definition.getBeanClassName();
    LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
        + "' mapperInterface");
    // the mapper interface is the original class of the bean
    // but, the actual class of the bean is MapperFactoryBean
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    // 1.设置beanClass为MapperFactoryBean
    definition.setBeanClass(this.mapperFactoryBeanClass);
    definition.getPropertyValues().add("addToConfig", 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(
            () -> "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(
            () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
      }
      definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
      explicitFactoryUsed = true;
    }
    if (!explicitFactoryUsed) {
      LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
      // 2.设置autoWireMode=byType
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
    definition.setLazyInit(lazyInitialization);
  }
}
  1. 设置beanClass为MapperFactryBean

  2. 设置autoWireMode=byType

    1. No:即不启用自动装配。Autowire默认的值。 2. byName:通过属性的名字的方式查找JavaBean依赖的对象并为其注入。比如说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,然后使用Seter方法为其注入。 3. byType:通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用set方法为其注入。 4. constructor:通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用set方法注入,而是使用构造子注入。
public static final int AUTOWIRE_NO = 0;
public static final int AUTOWIRE_BY_NAME = 1;
public static final int AUTOWIRE_BY_TYPE = 2;
public static final int AUTOWIRE_CONSTRUCTOR = 3;

MapperFactoryBean相关

在这里插入图片描述

  1. 实现了FactoryBean接口,覆写了getObject( )方法,容器初始化时通过getObject()返回实际对象。
  2. 继承了SqlSessionDaoSupport类,有成员变量sqlSessionTemplate,通过autoWire=byType属性调用setSqlSessionFactory(sqlSessionFactory)初始化时,给sqlSessionTemplate赋值。
  3. getObject()方法通过Configuration对象获以JDK动态代理的方式获取代理类的实例。
  4. Configuration对象的mapperRegistry变量在Mybatis配置解析时赋值,mapperRegistry内部的knownMappers存储所有mapper接口的map信息,key为mapper接口的Class对象,value为持有对应Class对象的MapperProxyFactory实例,
  5. MapperProxyFactory是代理类工厂,用于生成代理对象MapperProxy的代理类,即为mapper接口的实际代理对象。
// MapperFactoryBean.java
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
  return this.sqlSessionTemplate;
}



// SqlSessionTemplate.java
@Override
public <T> T getMapper(Class<T> type) {
  return getConfiguration().getMapper(type, this);
}


// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}


// MapperRegistry.java
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);
        }
    }
}



// MapperProxyFactory.java
protected T newInstance(MapperProxy<T> mapperProxy) {
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}