MyBatis Spring 集成,mapper接口@repository有时候却不用写的原因(MyBatis Spring 集成源码解析)

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

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

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

之前看了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方法

来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » MyBatis Spring 集成,mapper接口@repository有时候却不用写的原因(MyBatis Spring 集成源码解析)

评论 抢沙发

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

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

支付宝扫一扫打赏

微信扫一扫打赏