【Spring源码】- 08 扩展点之mybatis集成

571 阅读12分钟
原文链接: mp.weixin.qq.com

概述

mybatis将与 spring集成的代码拆分到了mybatis-spring模块,避免 mybatisspring之间的耦合,如果你只需要纯粹的使用 mybatis api,就避免了必须将spring依赖也耦合进来的问题。 mybatis使用中一般是将Sql语句写在 xml文件中,为方便操作,我们会创建一个Mapper接口文件进行映射, mybatis提供了采用动态代理方式对Mapper接口类进行包装,这样我们就可以像使用普通对象一样执行各种方法调用。

mybatisspring集成的一个核心任务就是将这些动态代理包装的Mapper对象注入到 IoC容器中,这样其它Bean就可以方便的使用如 @Autowired等方式进行依赖注入。

MapperScannerConfigurer

需要将mybatis生成的动态代理对象注入到 IoC容器中,自然我们想到之前的BeanFactoryPostProcessor的子类 BeanDefinitionRegistryPostProcessor这个扩展类。MapperScannerConfigurer就是实现了 BeanDefinitionRegistryPostProcessor接口,然后在该接口中通过类扫描器scanner进行扫描注册。


                                            public void postProcessBeanDefinitionRegistry
                                                (BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) {     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);//指定引用的SqlSessionFactory    scanner.setSqlSessionTemplateBeanName(
                                                                    this.sqlSessionTemplateBeanName);    scanner.setResourceLoader(this.applicationContext);    scanner.setBeanNameGenerator(
                                                                        this.nameGenerator);    scanner.registerFilters();    //basePackage指定扫描Mapper接口包路径    scanner.scan(StringUtils.tokenizeToStringArray(
                                                                            this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}
                                                                                

ClassPathMapperScanner这个就是继承之前介绍过的 SpringClassPathBeanDefinitionScanner类扫描器进行了扩展,它可以实现将包路径下至少含有一个方法的接口类注册到 IoC中。

这里有个问题:注册进入的BeanDefinitionbeanClass指向的都是接口,到后续创建对象时会存在问题,接口是没法创建实例的。所以,ClassPathMapperScanner扫描器在注册完成后,又会对 BeanDefinition进行处理。处理逻辑位于ClassPathMapperScanner#processBeanDefinitions()方法中,其核心逻辑见下:


                                                                                    private void 
                                                                                        processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {    GenericBeanDefinition definition;    
                                                                                            for (BeanDefinitionHolder holder : beanDefinitions) {      definition = (GenericBeanDefinition) holder.getBeanDefinition();      String beanClassName = definition.getBeanClassName();      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
                                                                                                // issue #59      definition.setBeanClass(this.mapperFactoryBeanClass);   ...    }}
                                                                                                    

其中最重要的一条语句:definition.setBeanClass(this.mapperFactoryBeanClass),偷偷的将 BeanDefinitionbeanClass替换成了 MapperFactoryBean,而不再指向Mapper接口类。同时将 Mapper接口类作为参数传入到了MapperFactoryBean中,即调用下面构造方法:


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

MapperFactoryBean实现了 FactoryBean接口,这样实际上它是通过getObject()方法获取到对象然后注入到 IoC容器中。而在getObject()方法中,我们就可以使用mybatis api获取到 Mapper接口类的动态代理对象:SqlSession#getMapper()


                                                                                                                    public T getObject
                                                                                                                        () throws Exception {    
                                                                                                                            return getSqlSession().getMapper(this.mapperInterface);}
                                                                                                                                

上面我们分析了如何将Mapper接口类注入到 IoC容器中的实现思路,现在总结下主要有:

  1. 基于BeanDefinitionRegistryPostProcessor接口实现扩展,然后动态向 IoC容器中注入Bean
  2. 在注入时,会使用到ClassPathMapperScanner类扫描器将所有的 Mapper接口类解析成BeanDefinition集合注入;
  3. 为了解决接口不能创建对象问题,再注入后又将BeanDefinitionbeanClass替换成FactoryBean的实现类: MapperFactoryBean,在该实现类中通过mybatis apiSqlSession#getMapper()获取到Mapper接口的动态代理类

扩展点引入

通过MapperScannerConfigurer,解决了如何将 Mapper接口类注入到IoC容器的问题,现在还有另外一个问题,这个扩展点只有注册到 Spring中才会起作用,那又如何将其注册到Spring中呢?

方式一:最直接方式就是直接创建MapperScannerConfigurer类型的 Bean实例,比如:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">    <property name="basePackage" value="org.simon.demo01.mapper" />    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/></bean>

这种方式是最简单直接的,但是使用角度来说不方便,所以,mybatis-spring-1.2新增了两种方式: <mybatis-scan>标签方式和@MapperScan注解方式。

首先来看下<mybatis:scan>标签方式,添加 mybatisschema,然后就可以使用 <mybatis:scan base-package="org.simon.demo01.mapper"/>:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns="http://www.springframework.org/schema/beans"       xmlns:context="http://www.springframework.org/schema/context"        xmlns:p="http://www.springframework.org/schema/p"       xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context.xsd        http://mybatis.org/schema/mybatis-spring        http://mybatis.org/schema/mybatis-spring.xsd">    <!-- 自动扫描 -->    <context:component-scan base-package="org.simon.demo01" />    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=Asia/Shanghai" />        <property name="username" value="root" />        <property name="password" value="123456" />    </bean>    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        <property name="dataSource" ref="dataSource" />        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>    </bean>    <mybatis:scan base-package="org.simon.demo01.mapper"/></beans>

后台处理类NamespaceHandler<scan>标签注册解析器MapperScannerBeanDefinitionParser


                                                                                                                                                                    public class 
                                                                                                                                                                        NamespaceHandler extends 
                                                                                                                                                                            NamespaceHandlerSupport {  @Override  
                                                                                                                                                                                public void 
                                                                                                                                                                                    init() {    registerBeanDefinitionParser(
                                                                                                                                                                                        "scan", new MapperScannerBeanDefinitionParser());  }}
                                                                                                                                                                                            

再看下MapperScannerBeanDefinitionParser解析器:


                                                                                                                                                                                                protected AbstractBeanDefinition 
                                                                                                                                                                                                    parseInternal(Element element, ParserContext parserContext) {    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);    ClassLoader classLoader = ClassUtils.getDefaultClassLoader();    builder.addPropertyValue(
                                                                                                                                                                                                        "processPropertyPlaceHolders", true);    
                                                                                                                                                                                                            try {      String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION);      
                                                                                                                                                                                                                if (StringUtils.hasText(annotationClassName)) {        
                                                                                                                                                                                                                    @SuppressWarnings("unchecked")        Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) classLoader            .loadClass(annotationClassName);        builder.addPropertyValue(
                                                                                                                                                                                                                        "annotationClass", annotationClass);      }      String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE);      
                                                                                                                                                                                                                            if (StringUtils.hasText(markerInterfaceClassName)) {        Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName);        builder.addPropertyValue(
                                                                                                                                                                                                                                "markerInterface", markerInterface);      }      String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR);      
                                                                                                                                                                                                                                    if (StringUtils.hasText(nameGeneratorClassName)) {        Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);        BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);        builder.addPropertyValue(
                                                                                                                                                                                                                                        "nameGenerator", nameGenerator);      }      String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS);      
                                                                                                                                                                                                                                            if (StringUtils.hasText(mapperFactoryBeanClassName)) {        
                                                                                                                                                                                                                                                @SuppressWarnings(
                                                                                                                                                                                                                                                    "unchecked")        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = (Class<? extends MapperFactoryBean>) classLoader            .loadClass(mapperFactoryBeanClassName);        builder.addPropertyValue(
                                                                                                                                                                                                                                                        "mapperFactoryBeanClass", mapperFactoryBeanClass);      }    } 
                                                                                                                                                                                                                                                            catch (Exception ex) {      XmlReaderContext readerContext = parserContext.getReaderContext();      readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());    }    builder.addPropertyValue(
                                                                                                                                                                                                                                                                "sqlSessionTemplateBeanName", element.getAttribute(ATTRIBUTE_TEMPLATE_REF));    builder.addPropertyValue(
                                                                                                                                                                                                                                                                    "sqlSessionFactoryBeanName", element.getAttribute(ATTRIBUTE_FACTORY_REF));    builder.addPropertyValue(
                                                                                                                                                                                                                                                                        "lazyInitialization", element.getAttribute(ATTRIBUTE_LAZY_INITIALIZATION));    builder.addPropertyValue(
                                                                                                                                                                                                                                                                            "basePackage", element.getAttribute(ATTRIBUTE_BASE_PACKAGE));    
                                                                                                                                                                                                                                                                                return builder.getBeanDefinition();  }
                                                                                                                                                                                                                                                                                    

最关键的就是第一句 BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);,又是将 MapperScannerConfigurer动态注入到 Spring中,下面一堆都是解析标签属性进行依赖注入。

再来看下 @MapperScan注解方式,如: @MapperScan(basePackages = "org.simon.demo01.mapper")

                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                            @Retention(RetentionPolicy.RUNTIME)
                                                                                                                                                                                                                                                                                                                                @Target(ElementType.TYPE)
                                                                                                                                                                                                                                                                                                                                    @Documented
                                                                                                                                                                                                                                                                                                                                        @Import(MapperScannerRegistrar.class)
                                                                                                                                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                                                                                                                                @interface MapperScan {}
                                                                                                                                                                                                                                                                                                                                                    

@MapperScan注解上面使用了使用了一种非常常见的扩展方式: @Import扩展。通过 @Import注解,引入了 MapperScannerRegistrar,它是 ImportBeanDefinitionRegistrar类型,通常和 @Import注解组合使用,实现动态注入功能:

                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                                                                                                            @Override  
                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                    public 
                                                                                                                                                                                                                                                                                                                                                                                                        void 
                                                                                                                                                                                                                                                                                                                                                                                                            registerBeanDefinitions
                                                                                                                                                                                                                                                                                                                                                                                                                (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    
                                                                                                                                                                                                                                                                                                                                                                                                                    //获取注解上属性    AnnotationAttributes mapperScanAttrs = AnnotationAttributes        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));    
                                                                                                                                                                                                                                                                                                                                                                                                                        if (mapperScanAttrs != 
                                                                                                                                                                                                                                                                                                                                                                                                                            null) {      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 
                                                                                                                                                                                                                                                                                                                                                                                                                                0));    }  }  
                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                        void 
                                                                                                                                                                                                                                                                                                                                                                                                                                            registerBeanDefinitions
                                                                                                                                                                                                                                                                                                                                                                                                                                                (AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {    
                                                                                                                                                                                                                                                                                                                                                                                                                                                    //创建一个MapperScannerConfigurer的BeanDefinition    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);    
                                                                                                                                                                                                                                                                                                                                                                                                                                                        /**     * 下面就是解析注解属性值,通过PropertyValue方式进行依赖注入到Bean中     */    builder.addPropertyValue(
                                                                                                                                                                                                                                                                                                                                                                                                                                                            "processPropertyPlaceHolders", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                true);    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);    }    ...
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            //各种依赖注入    builder.addPropertyValue(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    //将生成的BeanDefinition注册到IoC中    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

方法中同样有 BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)这句,动态的将 MapperScannerConfigurer注入到 Spring中,然后是一堆的解析注解属性进行依赖注入,这样通过 @Import+ ImportBeanDefinitionRegistrar动态注入,就实现了将 MapperScannerConfigurer扩展点注册到 Spring中。

SpringBoot自动装配

不论是通过 <mybatis:scan>标签方式,还是 @MapperScan注解方式,这些是常规的第三方模块与 Spring进行集成方式。这种集成方式比较繁琐的是:你不光要通过 <mybatis:scan>@MapperScan注解将第三方集成进来,你还需要初始化一些依赖对象,比如这里的 DataSourceSqlSessionFactory等。当一个项目集成了很多第三方模块时,每个模块都这样搞一下,配置的工作量就大了,比如最常使用的 ssm集成配,传统 Spring集成要搞一大堆配置。

所以, SpringBoot提出了一个比较优秀的思想:自动装配。需要什么模块直接把依赖添加进来,自动完成装配,对于个性化可以在属性文件中进行配置,从使用角度来说,即插即用,不需要有太多的编码。第三方程序和 spring就像完全融入一体一样,简化项目构建时集成成本,也降低项目配置的复杂性,所以 SpringBoot会被越来越多的项目所采用,进而也推动微服务的兴起。

SpringBoot中使用 mybatis,直接依赖 mybatis-spring-boot-starter,它会把 mybatismybatis-springmybatis-spring-boot-autoconfigure三个依赖包都添加进来。前面两个依赖包好理解,这里关键是第三个依赖包,就是通过它实现了 mybatis自动装配功能。下面我们来看下 SpringBoot是如何实现 mybatis的主动装配。

1、首先,定义一个 mybatis主动装配配置类,如下:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @org.springframework.context.annotation.Configuration
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                @ConditionalOnSingleCandidate(DataSource.class)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @EnableConfigurationProperties(MybatisProperties.class)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    class 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        MybatisAutoConfiguration 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            implements 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                InitializingBean {  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            MybatisAutoConfiguration
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                (MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,      ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,      ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,      ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) { ...  }  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @Bean  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @ConditionalOnMissingBean  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public SqlSessionFactory 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    sqlSessionFactory
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        (DataSource dataSource) 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            throws Exception {    SqlSessionFactoryBean factory = 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                new SqlSessionFactoryBean();    factory.setDataSource(dataSource);    factory.setVfs(SpringBootVFS.class);    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    if (StringUtils.hasText(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        this.properties.getConfigLocation())) {      factory.setConfigLocation(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            this.resourceLoader.getResource(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                this.properties.getConfigLocation()));    }    applyConfiguration(factory);    ...
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    //省略一堆配置    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        if (factoryPropertyNames.contains(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "defaultScriptingLanguageDriver")) {      factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);    }    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                return factory.getObject();  }  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @Bean  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @ConditionalOnMissingBean  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public SqlSessionTemplate 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    sqlSessionTemplate
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        (SqlSessionFactory sqlSessionFactory) {    ExecutorType executorType = 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            this.properties.getExecutorType();    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if (executorType != 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    null) {      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        return 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            new SqlSessionTemplate(sqlSessionFactory, executorType);    } 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                else {      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    return 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        new SqlSessionTemplate(sqlSessionFactory);    }  }    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                static 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        class 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            AutoConfiguredMapperScannerRegistrar 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                implements 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    BeanFactoryAware, 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        ImportBeanDefinitionRegistrar {    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            private BeanFactory beanFactory;    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                @Override    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                registerBeanDefinitions
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        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));      }      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);      builder.addPropertyValue(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        "processPropertyPlaceHolders", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            true);      builder.addPropertyValue(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "annotationClass", Mapper.class);      builder.addPropertyValue(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "basePackage", StringUtils.collectionToCommaDelimitedString(packages));      BeanWrapper beanWrapper = 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        new BeanWrapperImpl(MapperScannerConfigurer.class);      Stream.of(beanWrapper.getPropertyDescriptors())          .filter(x -> x.getName().equals(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "lazyInitialization")).findAny()          .ifPresent(x -> builder.addPropertyValue(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "lazyInitialization", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "${mybatis.lazy-initialization:false}"));      registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());    }    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @Override    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        setBeanFactory
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            (BeanFactory beanFactory) {      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                this.beanFactory = beanFactory;    }  }  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @org.springframework.context.annotation.Configuration  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @Import(AutoConfiguredMapperScannerRegistrar.class)  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })  
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    static 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            class 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                MapperScannerRegistrarNotFoundConfiguration 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    implements 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        InitializingBean {  }}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

这里主要利用 MapperScannerRegistrarNotFoundConfiguration类上的 @Import(AutoConfiguredMapperScannerRegistrar.class)引入,然后在 AutoConfiguredMapperScannerRegistrarBeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class)这句又是动态注入 MapperScannerConfigurer。不过,主动装配配置类中,还会把相关的依赖也一起创建、初始化,比如: SqlSessionFactorySqlSessionTemplate

@EnableConfigurationProperties(MybatisProperties.class)mybatis相关配置引入进来,这样在创建、初始化过程中的定制需求就可以通过配置修改。

2、有了这个主动装配配置类还不行,下一步就是看如何让主动装配配置类生效。SpringBoot提供了 SpringFactoriesLoader工厂加载机制,类似于 JDK中的 SPI机制,实现将模块 META-INF/spring.factories文件中配置注入到 Spring容器中。 mybatis-spring-boot-autoconfigure模块下 META-INF/spring.factories文件中就有 MybatisAutoConfiguration

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

3、使用时依赖添加进来,配置下属性,就可以直接使用,基本不再需要编码:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    mybatis.mapper-locations:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        classpath:mapper/*Mapper.xmlmybatis.type-aliases-package:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        com.example.demo.entity
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

总结

从上面来看, mybatisspring集成的关键的是将 mybatis-spring模块下 MapperScannerConfigurer集成进来,因为,它是一个 BeanDefinitionRegistryPostProcessor类型的扩展,内部通过自定义 scanner扫描 Mapper接口自动注册到 IoC容器中,这一点在各种集成方式中是统一一样的。不同点在于: MapperScannerConfigurer扩展类是如何被引入的。传统的 Spring方式通过 @Mapper注解或 <mybatis:scan>自定义标签实现,但是对于一些依赖对象还是需要手工创建,比较繁琐;而 SpringBoot利用自动装配,让第三方模块集成变成了一个插件,即插即用,无需太多编码。

分析了 mybatis集成方式,从中也学习了如何利用 Spring的各种扩展点进行定制,更重要的是也为我们开发自己模块和 Spring集成提供了思路。

 长按识别关注, 持续输出原创