美文网首页
Mybatis源码分析(一)MapperProxy 的初始化

Mybatis源码分析(一)MapperProxy 的初始化

作者: 大杯冰摩卡 | 来源:发表于2020-08-31 10:17 被阅读0次

    本文源代码来源于mybatis-spring-boot-starter的2.1.2版本

    一、前言

    我们用Spring整合mybatis的时候一定见过这两个注解

    • @Mapper 使用在mapper接口上,将接口托管给Spring管理。
    • @MapperScan 用来开启包扫描,扫描项目某路径下的Mapper接口。

    1.1 @MapperScan

    @MapperScan 无疑更方便,让我们来看下它做了什么事情?

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(MapperScannerRegistrar.class)
    @Repeatable(MapperScans.class)
    public @interface MapperScan {
    
    /**
     * A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of MyBatis mapper scanning. Using
     * an @Enable annotation allows beans to be registered via @Component configuration, whereas implementing
     * {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
     *
     * @author Michael Lanyon
     * @author Eduardo Macarron
     * @author Putthiphong Boonphong
     *
     * @see MapperFactoryBean
     * @see ClassPathMapperScanner
     * @since 1.2.0
     */
    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    
      /**
       * {@inheritDoc}
       * 
       * @deprecated Since 2.0.2, this method not used never.
       */
      @Override
      @Deprecated
      public void setResourceLoader(ResourceLoader resourceLoader) {
        // NOP
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取@MapperScan注解上的属性
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
            registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
              generateBaseBeanName(importingClassMetadata, 0));
        }
      }
    
    

      MapperScan@Import了一个类MapperScannerRegistrar.class,它实现了ImportBeanDefinitionRegistrar接口,也就是说MapperScannerRegistrar拥有了向Spring注册Bean的能力。

      Spring在执行ConfigurationClassParserdoProcessConfigurationClass()方法的时候会用getImports()扫描@Import的类。在processImports()MapperScannerRegistrar会被放到importBeanDefinitionRegistrars列表中。后面就可以被ConfigurationClassBeanDefinitionReaderloadBeanDefinitionsForConfigurationClass()方法加载到了。

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    
    ……
    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                            // Candidate class is an ImportBeanDefinitionRegistrar ->
                            // delegate to it to register additional bean definitions
                            Class<?> candidateClass = candidate.loadClass();
                            ImportBeanDefinitionRegistrar registrar =
                                    BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                            ParserStrategyUtils.invokeAwareMethods(
                                    registrar, this.environment, this.resourceLoader, this.registry);
                            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                        }
    

    来看下Spring加载MapperScannerRegistrar回调的registerBeanDefinitions方法

      void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
          BeanDefinitionRegistry registry, String beanName) {
    
        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);
        }
       ……
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
    
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    
    
      }
    

    BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class) 构建了一个MapperScannerConfigurer.class类型的BeanDefinition(Spring中的bean),此外给它设置了几个属性:processPropertyPlaceHoldersannotationClassbasePackageannotationClass就是要扫描的Mapper的默认的注解类,basePackage就是要扫描的包的根路径。

    MapperScannerConfigurer.class,Spring整合Mybatis的核心类 它的作用是扫描项目中的Dao类,将其创建为Mybatis的Mapper对象也就是MapperProxy。这个我们一会儿会详细讲解。

    1.2 @Mapper

    SpringBoot整合Mybatis的时候,官方文档里提到Springboot可以自动扫描Mapper类

    • Auto-scan your mappers, link them to the SqlSessionTemplate and register them to Spring context so they can be injected into your beans

    我们对@Mapper find usage 可以发现MybatisAutoConfiguration这个类有使用到

    image

    进入MybatisAutoConfiguration可以看到MybatisAutoConfiguration实现了InitializingBean接口,那么就必然会执行afterPropertiesSet() 方法,我们在看下具体的实现

    @org.springframework.context.annotation.Configuration
    @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
    @ConditionalOnSingleCandidate(DataSource.class)
    @EnableConfigurationProperties(MybatisProperties.class)
    @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
    public class MybatisAutoConfiguration implements InitializingBean {
    
     /**
       * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan
       * mappers based on the same component-scanning path as Spring Boot itself.
       */
      @org.springframework.context.annotation.Configuration
      @Import(AutoConfiguredMapperScannerRegistrar.class)
      @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
      public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    
        @Override
        public void afterPropertiesSet() {
          logger.debug(
              "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
        }
    
      }
    

    @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }) 不用我多说了吧,我们在看下如果没有这两个类 Import的AutoConfiguredMapperScannerRegistrar.class,和MapperScannerRegistrar.class 有异曲同工之妙。但是我还是建议大家使用@MapperScan,省事!

     public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
    
        private BeanFactory beanFactory;
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
         ……
          BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
          builder.addPropertyValue("processPropertyPlaceHolders", true);
          builder.addPropertyValue("annotationClass", Mapper.class);
          builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
          BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
          Stream.of(beanWrapper.getPropertyDescriptors())
              // Need to mybatis-spring 2.0.2+
              .filter(x -> x.getName().equals("lazyInitialization")).findAny()
              .ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));
          registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
        }
    

    二、MapperScannerConfigurer

    MapperScannerConfigurer类图

    从MapperScannerConfigurer的类图我们可以了解它的实现情况

    • BeanNameAware:bean在创建的时候,会将bean的名称通过setBeanName()设置在bean中,这样外部程序就可以通过getBeanName()获取到bean的名称。
    • ApplicationContextAware:任意实现ApplicationContextAware的子类在被创建的时候,Spring会将ApplicationContext对象自动注入到当前bean中。
    • InitializingBean:InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括 afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。
    • BeanDefinitionRegistryPostProcessor:对标准BeanFactoryPostProcessorSPI的扩展,允许在常规BeanFactoryPostProcessor检测开始之前注册更多的bean定义。通常在我们想自定义BeanDefinition用到。MapperScannerConfigurer主要实现了扫描特定包并创建Mapper对象,并交给Spring管理。
    • BeanFactoryPostProcessor:BeanFactory的后置处理器,BeanFactoryPostProcessor在spring容器加载了bean的定义文件之后,在bean实例化之前,可以在postProcessBeanFactory()中根据需要对BeanDefinition进行修改,例如可以把bean的scope从singleton改为prototype。

    2.1 BeanDefinitionRegistryPostProcessor

    MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,因此Spring容器会在invokeBeanDefinitionRegistryPostProcessors()回调postProcessBeanDefinitionRegistry()方法。

      @Override
      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          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(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        scanner.registerFilters();
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    
    • 这里设置sqlSessionFactorysqlSessionTemplateBeanName,后续生成的Mapper自然会受到他们的管理
    • 调用了ClassPathMapperScanner的scan()方法

    2.2 ClassPathMapperScanner

    public int scan(String... basePackages) {
            int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    
            doScan(basePackages);
    
    
      @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;
      }
    
    • super.doScan()调用父类的doScan方法,解析配置文件并构建对应的BeanDefinitionHolder对象。
    • processBeanDefinitions() 增加BeanDefinitions定义,加入Mybatis特性

    2.2.1 doScan

    /**
         * Perform a scan within the specified base packages,
         * returning the registered bean definitions.
         * <p>This method does <i>not</i> register an annotation config processor
         * but rather leaves this up to the caller.
         * @param basePackages the packages to check for annotated classes
         * @return set of beans registered if any for tooling registration purposes (never {@code null})
         */
        protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
            Assert.notEmpty(basePackages, "At least one base package must be specified");
            Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
            for (String basePackage : basePackages) {
            //       扫描候选组件的类路径
                Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
                for (BeanDefinition candidate : candidates) {
                    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                    candidate.setScope(scopeMetadata.getScopeName());
                                //生成bean名称
                    String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                    if (candidate instanceof AbstractBeanDefinition) {
                        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                    }
                    if (candidate instanceof AnnotatedBeanDefinition) {
                        AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                    }
                    if (checkCandidate(beanName, candidate)) {
                        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                        definitionHolder =
                                AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                        beanDefinitions.add(definitionHolder);
                        //注册bean定义
                        registerBeanDefinition(definitionHolder, this.registry);
                    }
                }
            }
            return beanDefinitions;
        }
    

    在指定的基础包中执行扫描,返回已注册的bean定义。MapperScanner执行之前,doscan在processConfigBeanDefinitions()也会被执行到用来做Config的扫描。

    image
    可以参考spring 扫描BeanDefinition详解

    2.2.2 processBeanDefinitions

    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
         // 这里就设置了生成的Mappe的类是mapperFactoryBeanClass = MapperFactoryBean.class;
    
          definition.setBeanClass(this.mapperFactoryBeanClass);
    
          definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
          boolean explicitFactoryUsed = false;
          if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
          //将SqlSessionFactory放入MapperFactoryBean属性中,在实例化时可以自动获取到该SqlSessionFactory。
            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.");
            }
            //如果sqlSessionTemplate不为空,则放入到属性中,以便Spring在实例化MapperFactoryBean时可以得到对应的SqlSessionTemplate。
            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
          }
    
          if (!explicitFactoryUsed) {
            LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
          definition.setLazyInit(lazyInitialization);
        }
      }
    

    processBeanDefinitions()方法设置了BeanDefinition类为MapperFactoryBean,在Spring中我们可以通过FactoryBean对象的getObject()方法获得构建的实例。另外在MapperFactoryBean属性中还设置了SqlSessionFactorySqlSessionTemplate(sqlSessionTemplate不为空)。

    注意: 这里只是修改了BeanDefinition,Mapper还并未初始化。

    三、 MapperFactoryBean

    MapperFactoryBean

    3.1 DaoSupport

    
    /**
     * Generic base class for DAOs, defining template methods for DAO initialization.
     *
     * <p>Extended by Spring's specific DAO support classes, such as:
     * JdbcDaoSupport, JdoDaoSupport, etc.
     *
     * @author Juergen Hoeller
     * @since 1.2.2
     * @see org.springframework.jdbc.core.support.JdbcDaoSupport
     */
    public abstract class DaoSupport implements InitializingBean {
    
        /** Logger available to subclasses. */
        protected final Log logger = LogFactory.getLog(getClass());
    
    
        @Override
        public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
            // Let abstract subclasses check their configuration.
            checkDaoConfig();
    
            // Let concrete implementations initialize themselves.
            try {
                initDao();
            }
            catch (Exception ex) {
                throw new BeanInitializationException("Initialization of DAO failed", ex);
            }
        }
    

    通过文档注释可以知道,DaoSupport定义了初始化的模版方法

    • checkDaoConfig()检查或构建dao的配置信息
    /**
         * Abstract subclasses must override this to check their configuration.
         * <p>Implementors should be marked as {@code final} if concrete subclasses
         * are not supposed to override this template method themselves.
         * @throws IllegalArgumentException in case of illegal configuration
         */
    
    • initDao()初始化Dao相关的方法
    /**
         * Concrete subclasses can override this for custom initialization behavior.
         * Gets called after population of this instance's bean properties.
         * @throws Exception if DAO initialization fails
         * (will be rethrown as a BeanInitializationException)
         * @see org.springframework.beans.factory.BeanInitializationException
         */
    

    3.2 MapperFactoryBean

      @Override
      protected void checkDaoConfig() {
        super.checkDaoConfig();
    
        notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    
        Configuration configuration = getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
            configuration.addMapper(this.mapperInterface);
          } catch (Exception e) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
            throw new IllegalArgumentException(e);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }
    
    • mapperInterface 就只刚才扫描到的mapper接口
    • 如果已经没有注册过,就调用configuration.addMapper()把它加在configuration
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    ……
    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
      }
    

    我们来看下核心类MapperRegistry

    3.3 MapperRegistry

    public class MapperRegistry {
    
      private final Configuration config;
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    
    • 这里的Configuration是Mybatis的全局配置对象。包含各种xml或者注解解析的配置。
    • knownMappers放的是MapperMapperProxyFactory表示Mapper是否已经被添加。

    MapperRegistry中两个重要的方法addMappergetMapper。addMapper为*Mapper创建对应的MapperProxyFactory映射关系,getMapper顾名思义就是获取MapperProxyFactory对象

    3.3.1 addMapper

      public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
          if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
          }
          boolean loadCompleted = false;
          try {
            knownMappers.put(type, new MapperProxyFactory<>(type));
            // It's important that the type is added before the parser is run
            // otherwise the binding may automatically be attempted by the
            // mapper parser. If the type is already known, it won't try.
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
      }
    
    • 已注册的Mapper会抛出异常
    • Mapper创建MapperProxyFactory对象并放在map里
    • 如果Mapper对应的xml文件未加载,触发xml的绑定操作,将xml中的sql语句与Mapper建立关系。在后边会详细介绍。

    3.3.2 getMapper

      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
    
    • 根据类型从knownMappers获取相对应的MapperProxyFactory对象
    • newInstance(sqlSession)创建MapperProxy对象
      观察getMapper的堆栈调用情况不难发现,在扫描Mapper注入的时候回去调用MapperFactoryBeangetObject()方法生成Mapper的代理对象。MapperProxy的创建到这里就结束了。MapperProxy与XMl文件或注解中的sql语句建立关联,我们后面继续讨论。
      image

    相关文章

      网友评论

          本文标题:Mybatis源码分析(一)MapperProxy 的初始化

          本文链接:https://www.haomeiwen.com/subject/jwcusktx.html