根据Spring的工作原理,许多框架与Spring的整合本质上是将其他框架创建的对象纳入Spring的管理之下,使其成为Spring容器中的Bean。这样做的目的是为了让这些对象能够享受Spring框架提供的依赖注入、生命周期管理以及事务管理等功能。

以MyBatis为例,MyBatis可以作为一个独立的框架使用,这意味着你需要手动创建一些核心对象,如SqlSessionFactorySqlSession,并通过它们来执行SQL操作。但是,这种方式缺乏Spring所提供的便捷性和灵活性。

为了将MyBatis与Spring进行整合,我们需要将MyBatis的关键组件,如SqlSessionFactory,注册为Spring容器中的Bean。一旦这些组件成为Spring管理的Bean,就可以通过依赖注入的方式方便地获取和使用它们。这意味着在Spring项目中,开发者可以直接在Service层注入MyBatis的Mapper接口或SqlSessionTemplate等组件,而不需要关心这些对象是如何创建和管理的。

整合后的效果是,开发者可以更加专注于业务逻辑的编写,而Spring和MyBatis则共同承担了数据访问层的细节工作,提高了代码的可维护性和扩展性。此外,Spring的事务管理功能也可以无缝地应用于MyBatis的操作中,确保数据的一致性和完整性。

1 导入依赖


<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.4.RELEASE</version>
</parent>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
  <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.2</version>
  </dependency>

</dependencies>

2 @MapperScan作用


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//额外引入一个注册类 MapperScannerRegistrar
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
@AliasFor("basePackages")
String[] value() default {};

@AliasFor("value")
String[] basePackages() default {};

Class<?>[] basePackageClasses() default {};

Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

Class<? extends Annotation> annotationClass() default Annotation.class;

Class<?> markerInterface() default Class.class;

String sqlSessionTemplateRef() default "";

String sqlSessionFactoryRef() default "";

Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

String lazyInitialization() default "";

String defaultScope() default "";

boolean processPropertyPlaceHolders() default true;
}

//1 实现了ImportBeanDefinitionRegistrar接口 可以向spring容器注册bean定义 在bean工厂后处理器执行阶段会执行
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    public MapperScannerRegistrar() {
    }

    /** @deprecated */
    @Deprecated
    public void setResourceLoader(ResourceLoader resourceLoader) {
    }

    //2 注册bean定义
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //3 获取注解信息 主要是mapperscan的注解信息
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
            this.registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
        }

    }

    void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
        //4 注册MapperScannerConfigurer 这个类会记录mapperscan的一下信息
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", annoAttrs.getBoolean("processPropertyPlaceHolders"));
        Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
        if (!Annotation.class.equals(annotationClass)) {
            builder.addPropertyValue("annotationClass", annotationClass);
        }

        Class<?> markerInterface = annoAttrs.getClass("markerInterface");
        if (!Class.class.equals(markerInterface)) {
            builder.addPropertyValue("markerInterface", markerInterface);
        }

        Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
        if (!BeanNameGenerator.class.equals(generatorClass)) {
            builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
        }

        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
            builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
        }

        String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
        if (StringUtils.hasText(sqlSessionTemplateRef)) {
            builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
        }

        String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
        if (StringUtils.hasText(sqlSessionFactoryRef)) {
            builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
        }

        List<String> basePackages = new ArrayList();
        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
        basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
        if (basePackages.isEmpty()) {
            //5 获取mapper的扫描路径
            basePackages.add(getDefaultBasePackage(annoMeta));
        }

        String lazyInitialization = annoAttrs.getString("lazyInitialization");
        if (StringUtils.hasText(lazyInitialization)) {
            builder.addPropertyValue("lazyInitialization", lazyInitialization);
        }

        String defaultScope = annoAttrs.getString("defaultScope");
        if (!"".equals(defaultScope)) {
            builder.addPropertyValue("defaultScope", defaultScope);
        }

        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
        builder.setRole(2);
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    }

    private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
        return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index;
    }

    private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) {
        return ClassUtils.getPackageName(importingClassMetadata.getClassName());
    }

    static class RepeatingRegistrar extends MapperScannerRegistrar {
        RepeatingRegistrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AnnotationAttributes mapperScansAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
            if (mapperScansAttrs != null) {
                AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value");

                for(int i = 0; i < annotations.length; ++i) {
                    this.registerBeanDefinitions(importingClassMetadata, annotations[i], registry, MapperScannerRegistrar.generateBaseBeanName(importingClassMetadata, i));
                }
            }

        }
    }
}

//实现了BeanDefinitionRegistryPostProcessor接口 可以注册bean定义
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
        this.processPropertyPlaceHolders();
    }
    //1 创建ClassPathMapperScanner 这个对象重写可spring的扫描对象规则 可以扫描接口
    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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(this.lazyInitialization)) {
        scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
    }

    if (StringUtils.hasText(this.defaultScope)) {
        scanner.setDefaultScope(this.defaultScope);
    }
    //2 支持接口的扫描 对spring的默认扫描规则新增了接口扫描的扩展
    scanner.registerFilters();
    //3 调用扫描方法
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}

}

总结:

  • 首先在@mapperscan注解配置属性 其中最重要的是basePackages 同时会引入一个核心类-MapperScannerRegistrar

  • MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar 在spring启动阶段可以向bean工厂注册bean定义 在registerBeanDefinitions方法中主要封装了@mapperscan注解的属性 封装成MapperScannerConfigurer对象 注册到bean工厂中

  • MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口 在spring启动阶段会想bean工厂注册bean定义 在postProcessBeanDefinitionRegistry方法中 创建了ClassPathMapperScanner对象 这个对象继承了spring的扫描器 重写了spring的过滤规则以及扫描后置处理方法 支持扫描接口对象

3 扫描流程

核心类:ClassPathMapperScanner


public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    //1 扫描所有接口并封装成bean定义
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> {
            return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
        });
    } else {
        //2 由于接口不能实例化 需要将bean定义修改 
        //这里mybatis提供了一个实现factorybean接口的bean 用来创建接口的代理对象 
        this.processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}



private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    BeanDefinitionRegistry registry = this.getRegistry();
    Iterator var4 = beanDefinitions.iterator();

    while(var4.hasNext()) {
        BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
        AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
        boolean scopedProxy = false;
        if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
            definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> {
                return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]");
            });
            scopedProxy = true;
        }

        String beanClassName = definition.getBeanClassName();
        LOGGER.debug(() -> {
            return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface";
        });
        //1 添加构造方法参数 MapperFactoryBean的构造方法需要传入接口对象
        definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);

        try {
            definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
        } catch (ClassNotFoundException var10) {
        }
        //2 将bean类型修改为MapperFactoryBean
        definition.setBeanClass(this.mapperFactoryBeanClass);
        definition.getPropertyValues().add("addToConfig", this.addToConfig);
        definition.setAttribute("factoryBeanObjectType", beanClassName);
        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) {
                LOGGER.warn(() -> {
                    return "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) {
                LOGGER.warn(() -> {
                    return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.";
                });
            }

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

        if (!explicitFactoryUsed) {
            LOGGER.debug(() -> {
                return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.";
            });
            definition.setAutowireMode(2);
        }

        definition.setLazyInit(this.lazyInitialization);
        if (!scopedProxy) {
            if ("singleton".equals(definition.getScope()) && this.defaultScope != null) {
                definition.setScope(this.defaultScope);
            }

            if (!definition.isSingleton()) {
                BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
                if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
                    registry.removeBeanDefinition(proxyHolder.getBeanName());
                }
                //3 注册封装好的bean方法
                registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
            }
        }
    }

}

4 MapperFactoryBean

//将mybatis提供的代理对象注入到spring容器中
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    private Class<T> mapperInterface;
    private boolean addToConfig = true;

    public MapperFactoryBean() {
    }
    //代理的接口类
    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    protected void checkDaoConfig() {
        super.checkDaoConfig();
        Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
        Configuration configuration = this.getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
            try {
                configuration.addMapper(this.mapperInterface);
            } catch (Exception var6) {
                this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
                throw new IllegalArgumentException(var6);
            } finally {
                ErrorContext.instance().reset();
            }
        }

    }

    public T getObject() throws Exception {
        //获取mybatis的代理对象
        return this.getSqlSession().getMapper(this.mapperInterface);
    }

    public Class<T> getObjectType() {
        return this.mapperInterface;
    }

    public boolean isSingleton() {
        return true;
    }

    public void setMapperInterface(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
    }

    public boolean isAddToConfig() {
        return this.addToConfig;
    }
}

5 总结

为了清晰地描述Spring整合MyBatis时如何将Mapper接口注册为Bean的过程,我们可以按以下步骤重新梳理:

  • 通过@MapperScan注解导入MapperScannerRegistrar类。这个类负责在Spring启动时注册Mapper接口。

  • MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,因此在Spring启动期间,Spring会调用该类中的registerBeanDefinitions方法。

  • registerBeanDefinitions方法中,定义了一个ClassPathMapperScanner对象,用于扫描指定包下的Mapper接口。

  • ClassPathMapperScanner对象配置为可以扫描接口。由于Spring默认不会扫描接口,这里需要明确指出扫描规则。

  • ClassPathMapperScanner类重写了isCandidateComponent方法,使得只有接口被认为是候选Component。

  • 通过Spring的扫描机制,将接口扫描出来并获取对应的BeanDefinition

  • 对扫描得到的BeanDefinition进行修改,将BeanClass修改为MapperFactoryBean,并将AutowireMode设置为BY_TYPE

  • 扫描完成后,Spring将基于修改后的BeanDefinition创建Bean,即为每个Mapper创建一个对应的FactoryBean

  • MapperFactoryBeangetObject方法中,通过调用getSqlSession()方法获取SqlSession对象,并根据对应的Mapper接口生成一个Mapper代理对象。这个代理对象最终成为Spring容器中的Bean。

  • SqlSession对象是由MyBatis提供的,它的创建依赖于SqlSessionFactory

总结来说,通过@MapperScan注解,Spring在启动期间会识别并注册Mapper接口为Bean。这个过程涉及到了MapperScannerRegistrar类的使用,以及ClassPathMapperScanner对象的配置和扫描规则的设定。最终,通过MapperFactoryBean创建的Mapper代理对象成为Spring容器的一部分,可以像其他Bean一样被注入和使用。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐