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

 2023-02-13
原文作者:顾东流 原文地址:https://juejin.cn/post/7014412003930275870

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

原理说明

作用

根据@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容器中

时序图

202301012121564281.png

202301012121568752.png

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到容器中。

202301012121574373.png

关于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。

202301012121580394.png

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相关

202301012121586075.png

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