• SpringBoot与Mybatis整合方式01(源码分析)


    前言:入职新公司,SpringBoot和Mybatis都被封装了一次,光用而不知道原理实在受不了,于是开始恶补源码,由于刚开始比较浅,存属娱乐,大神勿喷。

    就如网上的流传的SpringBoot与Mybatis整合两种方式

    一.使用 pom文件使用:org.mybatis.spring.boot 依赖

    坐标:

    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>xxxxx</version>

    通过pom.xml能够看到该依赖的其他依赖文件

    注意这个org.mybatis.spring....autoconfigure包,找到这个包,点开META-INF目录下,找到spring.factories文件   这是自动配置mybatis注解,mapper存放位置等的地方。

    这里如果EnableAutoConfigurationImportSelector不懂的可以看 https://www.jianshu.com/p/464d04c36fb1

    ,这里不在详细解释,只要知道是Spring的一个自动配置的注解,通过它会自动加载类。

    进入 MybatisAutoConfiguration

    
    
    @Configuration
    @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
    @ConditionalOnBean({DataSource.class})
    @EnableConfigurationProperties({MybatisProperties.class})
    @AutoConfigureAfter({DataSourceAutoConfiguration.class})
    public class MybatisAutoConfiguration {
        private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
        private final MybatisProperties properties;
        private final Interceptor[] interceptors;
        private final ResourceLoader resourceLoader;
        private final DatabaseIdProvider databaseIdProvider;
        private final List<ConfigurationCustomizer>configurationCustomizers;
    MybatisProperties 类是接受Mybatis配置文件的类
    @ConfigurationProperties(
        prefix = "mybatis"
    )
    public class MybatisProperties {
        public static final String MYBATIS_PREFIX = "mybatis";
        private String configLocation;
        private String[] mapperLocations;
        private String typeAliasesPackage;
        private String typeHandlersPackage;
        private boolean checkConfigLocation = false;
        private ExecutorType executorType;
        private Properties configurationProperties;
        @NestedConfigurationProperty
        private Configuration configuration;
    config-location    Location of MyBatis xml config file.
    check-config-location    Indicates whether perform presence check of the MyBatis xml config file.
    mapper-locations    Locations of Mapper xml config file.
    type-aliases-package    Packages to search for type aliases. (Package delimiters are ",; 	
    ")
    type-handlers-package    Packages to search for type handlers. (Package delimiters are ",; 	
    ")
    executor-type    Executor type: SIMPLE, REUSE, BATCH.
    configuration-properties    Externalized properties for MyBatis configuration. Specified properties can be used as placeholder on MyBatis config file and Mapper file. For detail see the MyBatis reference page
    configuration    A MyBatis Configuration bean. About available properties see the MyBatis reference page. NOTE This property cannot be used at the same time with the config-location.

    @PostConstruct 在初始化的时候执行:加载配置文件
        @PostConstruct
        public void checkConfigFileExists() {
            if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
                Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
                Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
            }
    
        @Bea@ConditionalOnMissingBean 自动创建 sqlSessionFactorySqlSessionTemplate


    下面的静态内部类 AutoConfiguredMapperScannerRegistrar 会在运行期间@Mapper注解,使用过@Mapper的Mapper接口
    自动的生成动态代理mapper接口
    public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
            private BeanFactory beanFactory;
            private ResourceLoader resourceLoader;
    
            public AutoConfiguredMapperScannerRegistrar() {
            }
    
            public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
                ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    
                try {
                    if (this.resourceLoader != null) {
                        scanner.setResourceLoader(this.resourceLoader);
                    }
    
                    List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                    if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
                        Iterator var5 = packages.iterator();
    
                        while(var5.hasNext()) {
                            String pkg = (String)var5.next();
                            MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
                        }
                    }
              //注册注解
                    scanner.setAnnotationClass(Mapper.class);
                    scanner.registerFilters();
                    scanner.doScan(StringUtils.toStringArray(packages));
                } catch (IllegalStateException var7) {
                    MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", var7);
                }
    
            }
    
            public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
                this.beanFactory = beanFactory;
            }
    
            public void setResourceLoader(ResourceLoader resourceLoader) {
                this.resourceLoader = resourceLoader;
            }
        }

    注意:要是此时在SpringBoot启动类上使用了@MapperScan()注解,那么此时将进入@Import 导入的类,进行注册
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import({MapperScannerRegistrar.class})
    public @interface MapperScan {
        String[] value() default {};
    
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    
        Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    
        Class<? extends Annotation> annotationClass() default Annotation.class;
    
        Class<?> markerInterface() default Class.class;
    
        String sqlSessionTemplateRef() default "";
    
        String sqlSessionFactoryRef() default "";
    
        Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
    }

    在 MapperScannerRegistrar的 registerBeanDefinitions 方法用ClassPathMapperScanner进行注册,使用doScan扫描包

    此时注册的注解需要annotationClass的值,

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
            if (this.resourceLoader != null) {
                scanner.setResourceLoader(this.resourceLoader);
            }
    
            Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
            if (!Annotation.class.equals(annotationClass)) {
                scanner.setAnnotationClass(annotationClass);
            }
    
            Class<?> markerInterface = annoAttrs.getClass("markerInterface");
            if (!Class.class.equals(markerInterface)) {
                scanner.setMarkerInterface(markerInterface);
            }
    
            Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
            if (!BeanNameGenerator.class.equals(generatorClass)) {
                scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
            }
    
            Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
            if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
                scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
            }
    
            scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
            scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
            List<String> basePackages = new ArrayList();
            String[] var10 = annoAttrs.getStringArray("value");
            int var11 = var10.length;
    
            int var12;
            String pkg;
            for(var12 = 0; var12 < var11; ++var12) {
                pkg = var10[var12];
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
    
            var10 = annoAttrs.getStringArray("basePackages");
            var11 = var10.length;
    
            for(var12 = 0; var12 < var11; ++var12) {
                pkg = var10[var12];
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
    
            Class[] var14 = annoAttrs.getClassArray("basePackageClasses");
            var11 = var14.length;
    
            for(var12 = 0; var12 < var11; ++var12) {
                Class<?> clazz = var14[var12];
                basePackages.add(ClassUtils.getPackageName(clazz));
            }
    
            scanner.registerFilters();
            scanner.doScan(StringUtils.toStringArray(basePackages));
        }
    若既不使用@MapperScan 注解 也不使用@Mapper注解,则Mapper类不能生成代理对象,则在@Autowire注入时,会报错
     nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException

    二 使用Mybatis-Spring整合

    该整合jar包含的类

     

     spring.handlers中内容找到处理器org.mybatis.spring.config.NamespaceHandler

    http://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler

    处理器中会有解析器MapperScannerBeanDefinitionParser来解析这个xml

        public void init() {
            this.registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser());
        }

    在  MapperScannerBeanDefinitionParser进行注册

    public synchronized BeanDefinition parse(Element element, ParserContext parserContext) {
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(parserContext.getRegistry());
            ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
            XmlReaderContext readerContext = parserContext.getReaderContext();
            scanner.setResourceLoader(readerContext.getResourceLoader());
    
            String annotationClassName;
            String markerInterfaceClassName;
            String nameGeneratorClassName;
            try {
                annotationClassName = element.getAttribute("annotation");
                if (StringUtils.hasText(annotationClassName)) {
                    Class<? extends Annotation> markerInterface = classLoader.loadClass(annotationClassName);
                    scanner.setAnnotationClass(markerInterface);
                }
    
                markerInterfaceClassName = element.getAttribute("marker-interface");
                if (StringUtils.hasText(markerInterfaceClassName)) {
                    Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName);
                    scanner.setMarkerInterface(markerInterface);
                }
    
                nameGeneratorClassName = element.getAttribute("name-generator");
                if (StringUtils.hasText(nameGeneratorClassName)) {
                    Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName);
                    BeanNameGenerator nameGenerator = (BeanNameGenerator)BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class);
                    scanner.setBeanNameGenerator(nameGenerator);
                }
            } catch (Exception var11) {
                readerContext.error(var11.getMessage(), readerContext.extractSource(element), var11.getCause());
            }
    
            annotationClassName = element.getAttribute("template-ref");
            scanner.setSqlSessionTemplateBeanName(annotationClassName);
            markerInterfaceClassName = element.getAttribute("factory-ref");
            scanner.setSqlSessionFactoryBeanName(markerInterfaceClassName);
            scanner.registerFilters();
            nameGeneratorClassName = element.getAttribute("base-package");
            scanner.scan(StringUtils.tokenizeToStringArray(nameGeneratorClassName, ",; 	
    "));
            return null;
        }

    也可以使用@MapperScanner注解 在 MapperScannerConfigurer进行注册

     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            if (this.processPropertyPlaceHolders) {
                this.processPropertyPlaceHolders();
            }
    
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
            scanner.setAddToConfig(this.addToConfig);
            scanner.setAnnotationClass(this.annotationClass);
            scanner.setMarkerInterface(this.markerInterface);
            scanner.setSqlSessionFactory(this.sqlSessionFactory);
            scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
            scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
            scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
            scanner.setResourceLoader(this.applicationContext);
            scanner.setBeanNameGenerator(this.nameGenerator);
            scanner.registerFilters();
            scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; 	
    "));
        }

    还可以在 xml文档里注册 MapperScannerConfigurer自动管理(同上,一个xml一个注解)

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

    注意: org-mybatis-spring.boot为: 1.3.0  mybatis-spring:1.3.1 ,mybatis:3.4.4

  • 相关阅读:
    android音乐播放器开发 SweetMusicPlayer 摇一摇换歌
    kernel logo到开机动画之间闪现黑屏(android 5.X)
    [DLX反复覆盖] hdu 3656 Fire station
    使用清华源 tensorflow 安装
    类别不平衡问题之SMOTE算法(Python imblearn极简实现)
    在Ubuntu 18.04上安装OpenCV 4(C ++和Python)
    OpenCV
    git clone速度太慢的解决办法
    OpenCV 3.4.2 环境搭建(适用于Ubuntu 一键安装)
    ubuntu16.04安装opencv3.4.1教程
  • 原文地址:https://www.cnblogs.com/coding400/p/8120425.html
Copyright © 2020-2023  润新知