Spring整合mybatis源码流程

本文详细解释了Mybatis的@MapperScan如何与Spring框架集成,通过FactoryBean将Mapper接口转换为SpringBean,以及涉及到的关键类如MapperFactoryBean、SqlSessionFactory等在过程中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis的@MapperScan原理
第三方框架与Spring进行整合核心思想,是把其他框架所产生的对象放到Spring容器中,让其成为Bean。
像在spring中使用mybatis,开发人员只定义了mapper接口,而spring扫描候选component让其成为bean时,不会扫描接口。那如何让mapper接口变成bean,可以从容器中直接获取呢? 核心是利用FactoryBean接口的getObject()方法,返回一个mybatis的代理对象,并让它成为Bean放到spring容器中。 通过studentMmapper从容器中获取的是mybatis代理类的bean。

@Autowired
private StudentMapper studentMmapper;
  1. 在启动类或配置类上使用注解@MapperScan("com.xxx.mapper") 指明mapper接口的包路径
  2. 在MapperScan.class中,通过@Import导入类MapperScannerRegistrar,该类实现了接口ImportBeanDefinitionRegistrar,在spring容器启动时,会调用 MapperScannerRegistrar.registBeanDefinitions()方法
  3. registBeanDefinitions()会创建一个MapperScannerConfigurer类型的beanDefinition 【为其设置属性,并注册到BeanDefinitionRegistry】
  4. MapperScannerConfigurer实现了接口BeanDefinitionRegistryPostProcessor,spring启动时,会执行==MapperScannerConfigurer.postProcessBeanDefinitionRegistry()==方法
  5. postProcessBeanDefinitionRegistry()方法会创建ClassPathMapperScanner扫描器,该类是mybatis提供的扫描器,继承自spring的ClassPathBeanDefinitionScanner。设置ClassPathMapperScanner能扫描到接口,因为spring的扫描器只会扫描类。
  6. ClassPathMapperScan改写isCandidateComponent()设置可以扫描到接口
  @Override
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  }

spring的isCandidateComponent源码。该方法在类ClassPathScanningCandidateComponentProvider。ClassPathBeanDefinitionScanner继承了该类

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}
  1. 使用spring扫描方法ClassPathBeanDefinitionScanner.doScan(),得到对应的beanDefinition
  2. ClassPathMapperScanner.processBeanDefinitions()对扫描出来的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType。因为扫描得到的BeanDefinition类型是接口类型,不符合要求。
    在这里插入图片描述
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;

definition.setBeanClass(this.mapperFactoryBeanClass);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

在这里插入图片描述

  1. 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean。

  2. 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean

  3. sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生

  4. MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。

  5. 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性

  6. 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象

注册BeanDefinition的方式

//在启动类中注册
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(WjFactoryBean.class);
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(MemberMapper.class);
        ac.registerBeanDefinition("memberMapper", beanDefinition);

实现接口BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition.setBeanClass(MyFactoryBean.class);
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(MemberMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("memberMapper", beanDefinition);

        AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition1.setBeanClass(MyFactoryBean.class);
        beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(StudentMapper.class);
        beanDefinitionRegistry.registerBeanDefinition("studentMapper", beanDefinition1);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

实现ImportBeanDefinitionRegistrar.registBeanDefinitions(),需要通过@Import注解导入创建的类。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {

        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(WjMapperScan.class.getName());
        String path = (String)annotationAttributes.get("value");

        MyBeanDefinitionScanner scanner = new MyBeanDefinitionScanner(registry);
        scanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;
            }
        });
        scanner.scan(path);
    }
}


public class MyBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    public MyBeanDefinitionScanner (BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface();
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) {
            BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
            // 以下两行代码顺序不可调换
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
            beanDefinition.setBeanClassName(MyFactoryBean.class.getName());
        }
        return beanDefinitionHolders;
    }
}

BeanDefinitionRegistry就是spring容器。 它是个接口,DefaultListableBeanFactory实现了该接口。
在这里插入图片描述
参考:https://www.yuque.com/renyong-jmovm/spring/hzw30g

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值