美文网首页
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