MyBatis 原理:扫描 Mapper 接口

Mapper 扫描需要依赖 Maybtis/Spring 这个项目。

Mapper 扫描依赖两种方式:

  1. 通过 @Mapper 注解 (想通过该注解实现扫描 Mapper ,需要依赖 mybatis/spring-boot-starter 这个项目)
  2. 通过 @MapperScan 注解

无论是 @Mapper 还是 @MapeprScan 注解,底层都是需要去注册一个 MapperScannerConfigurer 的 Bean , 然后通过该 Bean 来实现 Mapper 的主动注入。

@Mapper 注解

@Mapper 注解主要使用在 Mapper 接口上,如果要实现只对 @Mapper 注解的接口实现扫描,则需要引入 mybatis/spring-boot-starter 这个项目。
mybatis/spring-boot-starter
使用方法请参考:https://github.com/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/site/zh/markdown/index.md
AutoConfiguredMapperScannerRegistrar 则是自动扫描 @Mapper 注解接口的实现类。
但是该类启用的条件,必须是 Spring 没有找到 MapperScannerConfigurer 和 MapperFactoryBean 的 bean 。

源码分析

AutoConfiguredMapperScannerRegistrar 是 MybatisAutoConfiguration 的静态内部类,故这里展示的是 MybatisAutoConfiguration 的源码。

/**
 * MybatisAutoConfiguration 是一个配置类 
 * 启用该配置类需要项目存在 SqlSessionFactory 和 SqlSessionFactoryBean 这两个类(这两个类都在 mybatis/spring 项目中)
 * mybatis/spring-boot-starter 会自动引入 mybatis/spring
 */
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
    // 忽略部分源码,这里只展示要讨论的部分

    /**
     * 对 @Mapper 注解自动扫描的实现类
     * 它继承了 ImportBeanDefinitionRegistrar 接口,实现了 registerBeanDefinitions() 方法
     * registerBeanDefinitions() 方法主要是创建并注册 MapperScannerConfigurer 的 BeanDefinition
     */
    public static class AutoConfiguredMapperScannerRegistrar
            implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {

        private BeanFactory beanFactory;
        private Environment environment;

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            // 如果没有配置自动扫描的包路径,那么则终止扫描 Mapper
            if (!AutoConfigurationPackages.has(this.beanFactory)) {
                logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
                return;
            }

            logger.debug("Searching for mappers annotated with @Mapper");
            // 获取自动扫描的路径
            List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
            if (logger.isDebugEnabled()) {
                packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
            }
            // 创建一个 MapperScannerConfigurer 的 BeanDefinitionBuilder
            // 并且设置 MapperScannerConfigurer 的字段,如 processPropertyPlaceHolders、annotationClass、basePackage 等字段
            // 后续 MapperScannerConfigurer 就会通过这些字段信息去扫描包
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
            builder.addPropertyValue("processPropertyPlaceHolders", true);
            // annotationClass 表示扫描的包中必须要有指定的注解,这里指定要有 Mapper 这个注解
            builder.addPropertyValue("annotationClass", Mapper.class);
            builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
            BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
            Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
                    .collect(Collectors.toSet());
            if (propertyNames.contains("lazyInitialization")) {
                // Need to mybatis-spring 2.0.2+
                builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
            }
            if (propertyNames.contains("defaultScope")) {
                // Need to mybatis-spring 2.0.6+
                builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
            }

            // 默认设置 sqlSessionTemplateBeanName 的值为 sqlSessionTemplate  
            boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
                    Boolean.TRUE);
            if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
                ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
                Optional<String> sqlSessionTemplateBeanName = Optional
                        .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
                Optional<String> sqlSessionFactoryBeanName = Optional
                        .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
                if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
                  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值