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

来源:互联网 发布:淘宝api是什么意思啊 编辑:程序博客网 时间:2024/05/19 13:45

        前面文章分析了这么多关于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中的,这里算是类的多态特性。

@Overridepublic 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转化为最基本的beanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);//过滤处理所有生成的基本beanDefinitionfor (BeanDefinition candidate : candidates) {//bean是单例还是原型判断ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());//取得beanNameString 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,是一个抽象类,同时又继承了另一个类



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


       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的源码分析。


阅读全文
0 0
原创粉丝点击