美文网首页
mybatis-spring解析

mybatis-spring解析

作者: Hogantry | 来源:发表于2019-04-13 16:44 被阅读0次

1、概述

原生Mybatis源码简析(上)
原生Mybatis源码简析(下)
在介绍原生Mybatis源码简析文章中,我们知道,Mapper接口的生命周期是在方法级别,方法执行结束,Mapper接口的动态代理实现类的生命就终结了。在下一次执行时,会再次重新生成新的代理类。但是当mybatis与spring结合使用时,mapper接口的代理实现类的生命周期是全局的,这是如何实现的呢?首先在spring中引入mybatis时,是需要加入mybatis-spring包的引用的。在spring的xml配置中需要加入如下配置:

<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->  
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
        <!-- 自动扫描mapping.xml文件 -->  
        <property name="mapperLocations" value="classpath:com/javen/mapping/*.xml"></property>  
    </bean>  
  
    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.javen.dao" />  
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>  
    </bean>  

那mapper接口的代理实现类的单例实现机制,定是与这两个bean有关了。

2、SqlSessionFactoryBean

SqlSessionFactoryBean,从名字就可看出他是一个FactoryBean,如此就直接看起getObject方法

public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }
public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

在getObject方法中调用了afterPropertiesSet方法,内部又调用了buildSqlSessionFactory方法,该方法内容很长,不再贴代码了,但方法实现比较简单,就是根据SqlSessionFactoryBean的众多配置属性来构建出Configuration对象,进而实例化SqlSessionFactory对象。与原厂mybatis的实现大同小异。

3、MapperScannerConfigurer

MapperScannerConfigurer类实现了BeanDefinitionRegistryPostProcessor接口,则重点看下postProcessBeanDefinitionRegistry方法的实现

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);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

ClassPathMapperScanner类是mybatis-spring框架提供的类,实现了spring框架的ClassPathBeanDefinitionScanner类。


image.png
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                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);
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

ClassPathBeanDefinitionScanner类的doScan方法会根据传入的basePackage,扫描包下的所有.class文件,并根据该类文件生成相应的BeanDefinition对象。针对mybatis的mapper接口,这里生成的BeanDefinition对象其实是半成品的对象,他的beanClass属性是接口的全限定类名。然后将半成品的BeanDefinition对象集合返回,在ClassPathMapperScanner类的processBeanDefinitions方法中,对这些半成品的BeanDefinition进行二次加工。

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
      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() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

重点是definition.setBeanClass(this.mapperFactoryBeanClass);这段方法。

private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;

MapperFactoryBean类也是一个FactoryBean,重置了beanClass属性之后,此时的BeanDefinition就是一个成品的了,之后实例化的时候,mapper接口对应的对象就是MapperFactoryBean的实例。如此便实现了mapper接口的全局单例话。
有空得再看看源码分析下,spring的事务是如何与mybatis的事务整合在一起的?

相关文章

网友评论

      本文标题:mybatis-spring解析

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