【mybatis系列】spring是如何整合mybatis将Mapper接口注册为Bean的

【mybatis系列】spring是如何整合mybatis将Mapper接口注册为Bean的

简单来说:

Spring整合MyBatis将Mapper接口注册为Bean的原理,主要是通过MapperScannerConfigurer (实现了BeanDefinitionRegistryPostProcessor接口)(或Spring Boot的自动配置)扫描Mapper接口,创建MapperFactoryBean的BeanDefinition实例,并注册到Spring容器中。MapperFactoryBean通过JDK动态代理为Mapper接口生成代理对象,从而实现了Mapper接口的Bean化。这一过程简化了开发者对数据库操作的管理,提高了开发效率。

参考如下流程:

1. 首先mybatis的mapper接口核心是 JDK动态代理
2. spring扫描类的时候会排除接口和抽象类,这样接口和抽象类是无法注册到IOC容器中
3. mybatis实现了BeanDefinitionRegistryPostProcessor 可以动态注册BeanDefinition
4. 需要自定义扫描器(继承spring内部扫描器 ClassPathBeanDefinitionScanner)重写排除接口的方法(isCandidateComponent)
5. 以上,就完成了接口注册成了BeanDefinition,但是无法实例化Bean,因为接口无法实例化
6 需要将BeanDefinition的BeanClass替换成JDK动态代理的实例对象(偷天换日)
7. mybatis通过FactoryBean的工厂方法设计模式,可以自由控制Bean的实例化过程

spring集成mybatis,核心点:JDK动态代理,BeanDefinitionRegistryPostProcessor, FactoryBean

Spring整合MyBatis将Mapper接口注册为Bean的原理,主要涉及到Spring的Bean注册机制以及MyBatis的Mapper接口处理。这一过程可以分为以下几个关键步骤:

1. 配置数据源和SqlSessionFactory

  • 在Spring的配置文件中(可以是XML配置、Java配置或注解配置),首先需要配置数据源(DataSource),该数据源会被注入到SqlSessionFactory中。
  • 使用SqlSessionFactoryBean来创建SqlSessionFactory实例。SqlSessionFactoryBean实现了FactoryBean接口,能够生成SqlSessionFactory对象。这个过程中,会配置MyBatis的配置文件(如mybatis-config.xml)以及Mapper映射文件等信息。

2. MapperScannerConfigurer扫描Mapper接口

  • MapperScannerConfigurer是Spring和MyBatis整合过程中一个重要的类,它实现了BeanDefinitionRegistryPostProcessor接口。Spring容器启动时会回调其postProcessBeanDefinitionRegistry方法。
  • MapperScannerConfigurer负责扫描指定包路径下的所有Mapper接口。对于每一个扫描到的Mapper接口,MapperScannerConfigurer会创建一个MapperFactoryBean的BeanDefinition实例,并将其注册到Spring容器中。这里,MapperFactoryBean的beanClass属性被设置为Mapper接口对应的类(但实际上是通过JDK动态代理来实现的)。

3. MapperFactoryBean生成代理对象

  • MapperFactoryBean是FactoryBean的一个实现,当Spring容器需要获取Mapper接口的Bean时,会调用MapperFactoryBean的getObject()方法。
  • getObject()方法内部会通过SqlSession的getMapper()方法,基于Mapper接口创建一个JDK动态代理对象。这个代理对象会拦截对Mapper接口方法的调用,并将这些调用转发给MyBatis执行。

4. @MapperScan注解的自动化配置(适用于Spring Boot)

  • 在Spring Boot项目中,通常不需要手动配置MapperScannerConfigurer,因为Spring Boot提供了自动配置。
  • 可以通过在配置类上使用@MapperScan注解来指定Mapper接口所在的包路径,Spring Boot会自动为这些Mapper接口创建Bean,并注册到Spring容器中。
  • 如果未使用@MapperScan注解,并且Mapper接口所在的包不在@ComponentScan的扫描范围内,则需要在Mapper接口上使用@Mapper注解来显式指定该接口是一个Mapper接口,以便Spring Boot能够自动识别并注册。

MapperScannerConfigurer.java
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
   ...

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

    ...

}    

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {



    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        Iterator var3 = beanDefinitions.iterator();

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

            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
            definition.setBeanClass(this.mapperFactoryBean.getClass());
            if (StringUtils.hasText(this.mapperHelperBeanName)) {
                definition.getPropertyValues().add("mapperHelper", new RuntimeBeanReference(this.mapperHelperBeanName));
            } else {
                if (this.mapperHelper == null) {
                    this.mapperHelper = new MapperHelper();
                }

                definition.getPropertyValues().add("mapperHelper", this.mapperHelper);
            }

            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);
            }
        }

    }

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }


}

  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值