Mybatis源码分析之Spring与Mybatis整合MapperScannerConfigurer处理过程源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

前面文章分析了这么多关于Mybatis源码解析,但是我们最终使用的却不是以前面文章的方式,编写自己mybatis_config.xml,而是最终将配置融合在spring的配置文件中。有了前面几篇博客的分析,相信这里会容易理解些关于Mybatis的初始化及其执行,但是仍旧需要Spring的很多知识,用到的时候会简略提到下。下面先看下我们具体使用Mybatis时候是怎样配置的。

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xmlns:mvc="http://www.springframework.org/schema/mvc"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans    
                            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
                            http://www.springframework.org/schema/context    
                            http://www.springframework.org/schema/context/spring-context-3.1.xsd    
                            http://www.springframework.org/schema/mvc    
                            http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  

        <!-- 引入配置文件 -->  
        <!-- <bean id="propertyConfigurer"  
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
            <property name="location" value="classpath:jdbc.properties" />  
        </bean>   -->
      <context:property-placeholder location="classpath:jdbc.properties"/>
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
            destroy-method="close">  
            <property name="driverClassName" value="${jdbc.driver}" />  
            <property name="url" value="${jdbc.url}" />  
            <property name="username" value="${jdbc.username}" />  
            <property name="password" value="${jdbc.password}" />  
            <!-- 初始化连接大小 -->  
            <property name="initialSize" value="${jdbc.initialSize}"></property>  
            <!-- 连接池最大数量 -->  
            <property name="maxActive" value="${jdbc.maxActive}"></property>  
            <!-- 连接池最大空闲 -->  
            <property name="maxIdle" value="${jdbc.maxIdle}"></property>  
            <!-- 连接池最小空闲 -->  
            <property name="minIdle" value="${jdbc.minIdle}"></property>  
            <!-- 获取连接最大等待时间 -->  
            <property name="maxWait" value="${jdbc.maxWait}"></property>  
        </bean>  

        <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->  
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
            <property name="dataSource" ref="dataSource" />  
            <!-- 自动扫描mapping.xml文件 -->  
            <property name="mapperLocations" value="classpath*:com/yzh/mapping/*.xml"/> 
            <property name="configurationProperties">
               <props>
                 <prop key="dialect">mysql</prop>
              </props>
            </property>
            <property name="plugins">
                <array>
                   <ref bean="pageInterceptor"/>
                </array>
            </property>
        </bean>  
       <!-- mybatis分页拦截器 -->
      <bean id = "pageInterceptor" class = "com.yzh.util.PageInterceptor"></bean>

        <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <property name="basePackage" value="com.yzh.dao" />  
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>  
        </bean>  

        <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->  
        <bean id="transactionManager"  
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
            <property name="dataSource" ref="dataSource" />  
        </bean>  

    </beans>  

这就是使用过程中的关于数据库相关的一个配置文件。里面主要涉及到下面两个Spring的初始化类。

SqlSessionFactoryBean类

负责Mybatis的初始化,最终会初始化出一个Configuration

MapperScannerConfigurer类

负责创建接口的代理,可以看到上面类实例化的时候传入的参数就是接口包

下面开始源码的具体分析。

(1)SqlSessionFactoryBean

从上面的配置文件可以看到这个类有几个属性,dataSource,plugin都比较熟悉,就不说了。主要讲解mapper.xml文件的初始化。

看一下这个类的继承结构

    public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent>

实现了三个接口,简要分析一下这三个接口。

1.FactoryBean

工厂bean,实例化的时候不是返回对象本身,而是调用它的方法getObject()方法返回的对象,如果要获取FactoryBean对象,可以在id前面加一个&符号来获取

     public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
          afterPropertiesSet();
        }

        return this.sqlSessionFactory;
      }

可以看到这个方法返回的是一个sqlSessionFactory(相信前面mybatis初始化的时候对这个类比较熟悉了),这个方法的逻辑也比较简单,如果这个属性没有初始化的化先执行初始化,初始化了直接返回,和假单例模式一样。

2.InitailizingBean

实现这个接口的bean,会在其实例化完成后,初始化阶段调用他的方法afterPropertiesSet()(可以看下spring bean的生命周期)

     public void afterPropertiesSet() throws Exception {
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

        this.sqlSessionFactory = buildSqlSessionFactory();
      }

可以看到这个方法的逻辑代码就是最后一句,也是从这一句开启了mapper.xml的初始化,这也就是mybatis和spirng结合以后初始化的入口,后面具体分析。

3.ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法。所以自己的类在实现该接口的时候,要实装该方法。如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到 ApplicationContext时,这个bean得到通知。其实这就是标准的Oberver设计模式。

      public void onApplicationEvent(ApplicationEvent event) {
        if (failFast && event instanceof ContextRefreshedEvent) {
          // fail-fast -> check all statements are completed
          this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
        }
      }

这个不是分析的重点,有兴趣的自行查下。

下面从初始化入口开启分析:

    this.sqlSessionFactory = buildSqlSessionFactory();

还记得前面一篇初始化配置文件为Configuration类的博客吗,整过过程切实初始化出来就是构建了SqlSessionFactory,SqlSessionFactory里包含Configuration。看这个方法的名字也是做的这件事,只是入口的方式不一样,下面看看是怎样归到一起的。

    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

        Configuration configuration;

        XMLConfigBuilder xmlConfigBuilder = null;
        if (this.configLocation != null) {
          xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
          configuration = xmlConfigBuilder.getConfiguration();
        } else {
          if (logger.isDebugEnabled()) {
            logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
          }
          configuration = new Configuration();
          configuration.setVariables(this.configurationProperties);
        }

        if (this.objectFactory != null) {
          configuration.setObjectFactory(this.objectFactory);
        }

        if (this.objectWrapperFactory != null) {
          configuration.setObjectWrapperFactory(this.objectWrapperFactory);
        }

        if (hasLength(this.typeAliasesPackage)) {
          String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
          for (String packageToScan : typeAliasPackageArray) {
            configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                    typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
            if (logger.isDebugEnabled()) {
              logger.debug("Scanned package: '" + packageToScan + "' for aliases");
            }
          }
        }

        if (!isEmpty(this.typeAliases)) {
          for (Class<?> typeAlias : this.typeAliases) {
            configuration.getTypeAliasRegistry().registerAlias(typeAlias);
            if (logger.isDebugEnabled()) {
              logger.debug("Registered type alias: '" + typeAlias + "'");
            }
          }
        }

        if (!isEmpty(this.plugins)) {
          for (Interceptor plugin : this.plugins) {
            configuration.addInterceptor(plugin);
            if (logger.isDebugEnabled()) {
              logger.debug("Registered plugin: '" + plugin + "'");
            }
          }
        }

        if (hasLength(this.typeHandlersPackage)) {
          String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
          for (String packageToScan : typeHandlersPackageArray) {
            configuration.getTypeHandlerRegistry().register(packageToScan);
            if (logger.isDebugEnabled()) {
              logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
            }
          }
        }

        if (!isEmpty(this.typeHandlers)) {
          for (TypeHandler<?> typeHandler : this.typeHandlers) {
            configuration.getTypeHandlerRegistry().register(typeHandler);
            if (logger.isDebugEnabled()) {
              logger.debug("Registered type handler: '" + typeHandler + "'");
            }
          }
        }

        if (xmlConfigBuilder != null) {
          try {
            xmlConfigBuilder.parse();

            if (logger.isDebugEnabled()) {
              logger.debug("Parsed configuration file: '" + this.configLocation + "'");
            }
          } catch (Exception ex) {
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
          } finally {
            ErrorContext.instance().reset();
          }
        }

        if (this.transactionFactory == null) {
          this.transactionFactory = new SpringManagedTransactionFactory();
        }

        Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
        configuration.setEnvironment(environment);

        if (this.databaseIdProvider != null) {
          try {
            configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
          } catch (SQLException e) {
            throw new NestedIOException("Failed getting a databaseId", e);
          }
        }

        if (!isEmpty(this.mapperLocations)) {
          for (Resource mapperLocation : this.mapperLocations) {
            if (mapperLocation == null) {
              continue;
            }

            try {
              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());
              xmlMapperBuilder.parse();
            } catch (Exception e) {
              throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
            } finally {
              ErrorContext.instance().reset();
            }

            if (logger.isDebugEnabled()) {
              logger.debug("Parsed mapper file: '" + mapperLocation + "'");
            }
          }
        } else {
          if (logger.isDebugEnabled()) {
            logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
          }
        }

        return this.sqlSessionFactoryBuilder.build(configuration);
      }

方法看起来有点长,其实逻辑很简单,就是将配置文件中的各个配置属性读入进来,因为它得对每个属性判空,所以显得很长,其实执行的只是配置了属性的那几项。

分析一下这个方法的逻辑,第一个if语句,调到了else分支实例化了一个Configuration类出来,下一步将数据库的初始化读入进来。

    if (this.transactionFactory == null) {
          this.transactionFactory = new SpringManagedTransactionFactory();
        }

        Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
        configuration.setEnvironment(environment);

接着就到了解析mapper文件的逻辑了:

    if (!isEmpty(this.mapperLocations)) {
          for (Resource mapperLocation : this.mapperLocations) {
            if (mapperLocation == null) {
              continue;
            }

            try {
              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());
              xmlMapperBuilder.parse();
            } catch (Exception e) {
              throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
            } finally {
              ErrorContext.instance().reset();
            }

            if (logger.isDebugEnabled()) {
              logger.debug("Parsed mapper file: '" + mapperLocation + "'");
            }
          }
        } 

mapperLocations可以以一个数组的方式将所有xml文件配置过来,然后逐步解析。这里其实就已经归到了前面博客的初始化过程了,后面就是逐个解析xml文件,解析每一个节点,将每个sql节点初始化为一个MappedStatement类,最终归入到Configuration里。初始化就讲解这么多。

(2) MapperScannerConfigurer
下面看这个类是怎么将接口创建为代理的。首先也看下这个类的继承结构。

    public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware

1.Aware接口

实现这些 Aware接口的Bean在被实例化之后,可以取得一些相对应的资源,例如实现BeanFactoryAware的Bean在实例化后,Spring容器将会注入BeanFactory的实例,而实现ApplicationContextAware的Bean,在Bean被实例化后,将会被注入 ApplicationContext的实例等等。

2.InitalizingBean

    public void afterPropertiesSet() throws Exception {
        notNull(this.basePackage, "Property 'basePackage' is required");
      }

没有什么逻辑,仅仅是一个对basePackage的判空操作

3.BeanDefinitionRegistryPostProcessor

这个接口:public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor。

实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置’order’属性来控制各个BeanFactoryPostProcessor的执行次序。注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。

这个类的执行就是从这个后处理开始的:

    /**
     * 
     * @param registry 这个参数是spring注册beanDefiniton的地方,这个类里面有一个缓存map,专门存储beanDefinition
     * @throws BeansException
     */
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //解决${jdbc.username}这种占位符的复制问题
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
        //这个类就是将接口扫描为BeanDefinition,并且最终被代理
        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.registerFilters();
        //扫描器的scan方法将basePackage扫描为beanDefinition
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }

感觉理解Mybatis的知识还是得具备一些spring源码的知识,不然略微会有点不知所云,至少应该知道spring bean的生命周期,BeanDefinition的感念。

    //逻辑转移到了doScan方法
    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;
    }

这里的doScan方法调用的是ClassPathMapperScanner中的,这里算是类的多态特性。

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //首先调用了父类的doScan方法,下面具体分析,比较关键,转化为了spring bean的形式
      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 {
        for (BeanDefinitionHolder holder : beanDefinitions) {
            //这里就转化为了正常的spring中的bean的形式了,spring就是最开始将配置文件初始化为了一个GenericBeanDefinition
          GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

          if (logger.isDebugEnabled()) {
            logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
                + "' and '" + definition.getBeanClassName() + "' mapperInterface");
          }

          // the mapper interface is the original class of the bean
          // but, the actual class of the bean is MapperFactoryBean
          //这种就是对MapperFactoryBean中的一些属性的赋值,(可以看些BeanWrapperImpl的相关知识)
          definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
          //最最关键的一步,这就是beanDefintion的真正的类型,这里也就是将所有的接口类最终都按照MapperFactoryBean处理的
          definition.setBeanClass(MapperFactoryBean.class);

          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) {
              //在MapperFactoryBean中找了一下sqlSessionFactory这个属性,没有找到,他的父类SqlSessionDaoSupport中也没有这个属性
              //但是有sqlSession这个属性,其实这里给sqlSessionFactory赋值,就是调用他的set方法,看到这个类虽然没有这个属性,但是确实有这个
              //属性的set方法,玄机都在这个 set方法里,最终跟踪下去实例化了蛮多对象
            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) {
            if (logger.isDebugEnabled()) {
              logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
            }
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
          }
        }
      }

      return beanDefinitions;
    }

上面的方法特别注意两个地方,将所有的接口转化为MapperFactoryBean和为sqlSessionFactory赋值。这个方法结束以后就完成了将接口注册成为了spring中真正的bean了,但是还没有经历实例化,实例化的过程中会对其进行代理。

    //看返回值就看得出,将传入的dao接口,最终扫描为BeanDefinitionHolder,这个类里包含这BeanDefinition属性
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        //显然basePackages也是可以配置为数组形式的
        for (String basePackage : basePackages) {
            //靠这个函数就将basePackage转化为最基本的beanDefinition
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //过滤处理所有生成的基本beanDefinition
            for (BeanDefinition candidate : candidates) {
                //bean是单例还是原型判断
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                //取得beanName
                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)) {
                    //封装到holder类中
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    //注册到registry中
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
        try {
            //basePackage路径构造
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + "/" + this.resourcePattern;
            //将路径转化为资源
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            boolean traceEnabled = logger.isTraceEnabled();
            boolean debugEnabled = logger.isDebugEnabled();
            //遍历每一个文件资源
            for (Resource resource : resources) {
                if (traceEnabled) {
                    logger.trace("Scanning " + resource);
                }
                if (resource.isReadable()) {
                    try {
                        //对资源内容的一个转化
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        if (isCandidateComponent(metadataReader)) {
                            //用传入的资源构建出基本的BeanDefinition,初始化bean的名称,注解等一些基本字段
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setResource(resource);
                            sbd.setSource(resource);
                            if (isCandidateComponent(sbd)) {
                                if (debugEnabled) {
                                    logger.debug("Identified candidate component class: " + resource);
                                }
                                candidates.add(sbd);
                            }
                            else {
                                if (debugEnabled) {
                                    logger.debug("Ignored because not a concrete top-level class: " + resource);
                                }
                            }
                        }
                        else {
                            if (traceEnabled) {
                                logger.trace("Ignored because not matching any filter: " + resource);
                            }
                        }
                    }
                    catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to read candidate component class: " + resource, ex);
                    }
                }
                else {
                    if (traceEnabled) {
                        logger.trace("Ignored because not readable: " + resource);
                    }
                }
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }

到这里就从细节上完成了接口注册为bean的过程了,下面所有的动作就只能在MapperFactoryBean里了,下面详细分析这个类。

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

      private Class<T> mapperInterface;

      private boolean addToConfig = true;

      /**
       * Sets the mapper interface of the MyBatis mapper
       *
       * @param mapperInterface class of the interface
       */
      public void setMapperInterface(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }

      /**
       * If addToConfig is false the mapper will not be added to MyBatis. This means
       * it must have been included in mybatis-config.xml.
       * <p>
       * If it is true, the mapper will be added to MyBatis in the case it is not already
       * registered.
       * <p>
       * By default addToCofig is true.
       *
       * @param addToConfig
       */
      public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
      }

      /**
       * {@inheritDoc}
       */
      @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 (Throwable t) {
            logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
            throw new IllegalArgumentException(t);
          } finally {
            ErrorContext.instance().reset();
          }
        }
      }

      /**
       * {@inheritDoc}
       */
      public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }

      /**
       * {@inheritDoc}
       */
      public Class<T> getObjectType() {
        return this.mapperInterface;
      }

      /**
       * {@inheritDoc}
       */
      public boolean isSingleton() {
        return true;
      }

    }

相信大家也看出来了这个类上的玄机,继承了一个类,同时实现了FactoryBean。首先看下继承的这个类,

public abstract class SqlSessionDaoSupport extends DaoSupport,是一个抽象类,同时又继承了另一个类

20191102100781\_1.png

感觉以这种截图的方式讲解还蛮清晰的。看到这个类是不是明白了,最终相当于MapperFactoryBean实现了InitializingBean,看到afterProperties方法中调用了一个抽象方法,这个方法实在哪个子类中实现的呢?

20191102100781\_2.png

mybatis与sping结合的所有初始化就结束了,到这里不仅早就有了Configuration类,sqlSession也被初始化完成了,下面就是用sqlSession调用执行方法的起始,getMapper的时候了,那么这个是怎么处理的呢,上面看到MapperFactoryBean还继承了工厂bean,所以这个bean被实例化的时候会调用他的getObeject方法,在这个方法中移花接木,生成了代理。

     public T getObject() throws Exception {
        return getSqlSession().getMapper(this.mapperInterface);
      }

其实只有这么一句,不过也看到了通过接口,调用getMapper方法了,这样就又归到了前面博客执行接口方法的流程里了,在这个方法调用完最终返回的是一个传入接口的动态代理。

——————————————————————————-20170910凌晨2:09的分割线———————————————————————————-

没什么想说的,其实也没有想象中的那么逻辑复杂,只是需要一点spring源码的知识。下一步期望可以分析一下Mybatis的sqlNode的问题,感觉这里还是不行,还有就是下一个系列SpringMVC的源码分析。


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Mybatis源码分析之Spring与Mybatis整合MapperScannerConfigurer处理过程源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏