1. 程式人生 > >MyBatis Spring 整合,mapper介面@repository有時候卻不用寫的原因(MyBatis Spring 整合原始碼解析)

MyBatis Spring 整合,mapper介面@repository有時候卻不用寫的原因(MyBatis Spring 整合原始碼解析)

之前看了Spring原始碼之後,對Spring+MyBatis專案有個疑問,Dao層的mapper介面上@repository有時候卻不用寫,難道mapper包掃描生成代理類發生在service層的依賴注入之前嗎?要不然service層的類@Autowired mapper介面時會找不到例項啊?

後面讀了MyBatis Spring 整合原始碼MapperScannerConfigurer後解決了疑問。

先看一下MapperScannerConfigurer

Mybatis MapperScannerConfigurer 自動掃描 將Mapper介面生成代理注入到Spring

MapperFactoryBean的出現為了代替手工使用SqlSessionDaoSupport或SqlSessionTemplate編寫資料訪問物件(DAO)的程式碼,使用動態代理實現。

比如下面這個官方文件中的配置:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

org.mybatis.spring.sample.mapper.UserMapper是一個介面,我們建立一個MapperFactoryBean例項,然後注入這個介面和sqlSessionFactory(mybatis中提供的SqlSessionFactory介面,MapperFactoryBean會使用SqlSessionFactory建立SqlSession)這兩個屬性。

之後想使用這個UserMapper介面的話,直接通過spring注入這個bean,然後就可以直接使用了,spring內部會建立一個這個介面的動態代理。

當發現要使用多個MapperFactoryBean的時候,一個一個定義肯定非常麻煩,於是mybatis-spring提供了MapperScannerConfigurer這個類,它將會查詢類路徑下的對映器並自動將它們建立成MapperFactoryBean。

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>

這段配置會掃描org.mybatis.spring.sample.mapper下的所有介面,然後建立各自介面的動態代理類。

與Spring整合可以分為3個步驟. 
1. 把Java類對應的Mapper介面類納入Spring中的IOC容器管理。 
2. 把Java類對應的XML名稱空間新增到Mybatis中的Configuration類中的mapperRegistry(用於管理Mybatis的Mapper). 
3. 使用Spring中的IOC容器擴充套件FactoryBean獲取到Mapper的例項。(第一步納入Spring只是介面)

下面看一下MapperScannerConfigurer原始碼:

先看一下類的繼承關係

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor,
 InitializingBean, ApplicationContextAware, BeanNameAware

實現了BeanDefinitionRegistryPostProcessor介面

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

BeanDefinitionRegistryPostProcessor介面繼承了BeanFactoryPostProcessor

BeanDefinitionRegistryPostProcessor spring官方解釋是:允許在正常的BeanFactoryPostProcessor檢測開始之前註冊更多的自定義bean。特別是,BeanDefinitionRegistryPostProcessor可以註冊更多的bean定義,然後定義BeanFactoryPostProcessor例項。也就是說可以藉此方法實現自定義的bean。

BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor, 也就是說想實現自定義的bean 可以實現BeanDefinitionRegistryPostProcessor或者BeanFactoryPostProcessor中的方法。

下面看一下MapperScannerConfigurer中實現BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if (this.processPropertyPlaceHolders) {
            this.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.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }

scan方法呼叫的是類ClassPathBeanDefinitionScanner的scan方法

public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        this.doScan(basePackages);
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
    }

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
        String[] var3 = basePackages;
        int var4 = basePackages.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String basePackage = var3[var5];
            Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
            Iterator var8 = candidates.iterator();

            while(var8.hasNext()) {
                BeanDefinition candidate = (BeanDefinition)var8.next();
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
                }

                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
                }

                if (this.checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    this.registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }

        return beanDefinitions;
}
第三行this.doScan(basePackages)呼叫的並不是本類的doScan方法,而是ClassPathMapperScanner的doScan方法

兩個類的關係:

class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            Iterator i$ = beanDefinitions.iterator();

            while(i$.hasNext()) {
                BeanDefinitionHolder holder = (BeanDefinitionHolder)i$.next();
                GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
                }

                definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
                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) {
                    definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
                    explicitFactoryUsed = true;
                }

                if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
                    if (explicitFactoryUsed) {
                        this.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) {
                        this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
                    }

                    definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
                    explicitFactoryUsed = true;
                }

                if (!explicitFactoryUsed) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
                    }

                    definition.setAutowireMode(2);
                }
            }
        }

        return beanDefinitions;
    }
第二行Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);呼叫的又是類ClassPathBeanDefinitionScanner的doScan方法