博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于mybatis核心类,及与spring集成时引入的核心类
阅读量:2062 次
发布时间:2019-04-29

本文共 8013 字,大约阅读时间需要 26 分钟。

       开篇

         说一下写这篇文章的目的,是为了帮助大家梳理一下在spring环境下我们需要做哪些工作去集成mybatis,以及为什么这么去做。好了下面进入我们的本次议题。

mybatis的几个关键类

       首先说一下mybatis的几个关键核心类,及他们的之间的关系。第一个SqlSessionFactory,用来创建SqlSession接口的实例的,默认是DefaultSqlSessionFactory类型的对象。第二个是SqlSession,通过他我们可以管理Connection和获取mapper 映射器实例的动态代理对象,从而进行dao 查询.到此基础的核心类就介绍到这。

与spring集成时需要的操作

      接着说一下和spring集成时的一些操作

或者开启包扫描

@Configuration@MapperScan("org.mybatis.spring.sample.mapper")public class AppConfig {  // ...}

 

好,最后我们总结一下spring集成mybatis所做的两个操作:1 创建sqlSessionFactoryBean   2 通过MapperBeanFactory或自动扫描创建mapper.那么@MapperScan 这个注解为我们做了什么操作,就能够将mapper代理对象放到ioc容器中去呢?

整合实现原理

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Import({MapperScannerRegistrar.class})//引入这个类public @interface MapperScan {    String[] value() default {};    String[] basePackages() default {};    Class
[] basePackageClasses() default {}; Class
nameGenerator() default BeanNameGenerator.class; Class
annotationClass() default Annotation.class; Class
markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; Class
factoryBean() default MapperFactoryBean.class;}

 

//mybatis version : 1.3.1.jarpublic class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {    private ResourceLoader resourceLoader;    public MapperScannerRegistrar() {    }    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);       //此处进行包的扫描        scanner.doScan(StringUtils.toStringArray(basePackages));    }    public void setResourceLoader(ResourceLoader resourceLoader) {        this.resourceLoader = resourceLoader;    }}
public Set
doScan(String... basePackages) { Set
beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else {//这个这个函数的关键位置 this.processBeanDefinitions(beanDefinitions); } return beanDefinitions; }
private void processBeanDefinitions(Set
beanDefinitions) { Iterator var3 = beanDefinitions.iterator(); while(var3.hasNext()) { BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next(); GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition(); if (this.logger.isDebugEnabled()) { this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); }//将我们原有的bean信息中class 作为构造函数参数传入构造函数 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); //将我们扫描到的mapper的bean的信息中class 的信息替换为 mapperFactoryBean.class definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { if (this.logger.isDebugEnabled()) { this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } //并将这个bean设置为属性自动注入,这摸做的目的是将sessionfactory 注入进去 definition.setAutowireMode(2); } }

 现在我们容器中的mapper接口现在已经替换为了MapperFactoryBean了。既然他是一个FactoryBean,我们必然会调用.getObject()方法来获取mapper,那么我们在来看看MapperFactoryBean中做了什么?

public T getObject() throws Exception {//这通过 sqlSession 进行mapper 动态代理的创建,(1)但是sqlSession 是谁创建的呢?(2)默认的sqlSession并不是线程安全的,每个mapper一个mapperFactorybean会不会造成线程不安全问题呢?        return this.getSqlSession().getMapper(this.mapperInterface);    }

 

此时我们在关联上上面的设置自动注入属性就会清晰很多了。

//sqlSessionFactory属性注入时  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {        if (!this.externalSqlSession) {//创建一个SqlSessionTemplate ,我这边需要说明一下,他实现了sqlSession接口,并且它是线程安全的            this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);        }    }

到此为止,spring集成mybatis的所有代码流程梳理就介绍完了。

总结

我们来在总结一下:

1) 添加注解@MapperScan时引入MapperScannerRegistrar 

2)在MapperScannerRegistrar 中调用doScan获取扫描包的信息

3) doScan调用processBeanDefinitions()方法

4)在这里面将扫描的Mapper接口的BeanDefinition的信息进行篡改 ,class设置为mapperFactoryBean ,构造函数参数设置为mapper接口名称,设置自动属性注入

5) 自动属性注入时,会注入sqlSessionFctory时创建一个线程安全的sqlSession

6)当我们getBean获取mapper时,会通过getObject方法通过sqlSession.getMapper()方法获取我们所需要的动态代理对象

在spring-boot中的表现

ok,这个MyBatis-spring集成的东西就这样吧。下面我们在来看看springBoot中是如何做的。

//mybatis-spring-boot-autoconfigure version 1.3.0.jar@Configuration//mybatis的属性对象封装类,并放入我们bean中@EnableConfigurationProperties({MybatisProperties.class})public class MybatisAutoConfiguration {    ...    ...    ...//创建SqlSessionFactory     @Bean    @ConditionalOnMissingBean    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();     ...        return factory.getObject();    }//创建SqlSessionTemplate    @Bean    @ConditionalOnMissingBean    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {        ExecutorType executorType = this.properties.getExecutorType();        return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);    }    @Configuration//引入AutoConfiguredMapperScannerRegistrar    @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})    @ConditionalOnMissingBean({MapperFactoryBean.class})    public static class MapperScannerRegistrarNotFoundConfiguration {      ...这里面没有什么卵用    }    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);                //这又是之前我们的逻辑了                scanner.doScan(StringUtils.toStringArray(packages));          }    }}

 

转载地址:http://ozelf.baihongyu.com/

你可能感兴趣的文章
Leetcode C++《热题 Hot 100-45》338.比特位计数
查看>>
读书摘要系列之《kubernetes权威指南·第四版》第一章:kubernetes入门
查看>>
Leetcode C++《热题 Hot 100-46》739.每日温度
查看>>
Leetcode C++《热题 Hot 100-47》236.二叉树的最近公共祖先
查看>>
Leetcode C++《热题 Hot 100-48》406.根据身高重建队列
查看>>
《kubernetes权威指南·第四版》第二章:kubernetes安装配置指南
查看>>
Leetcode C++《热题 Hot 100-49》399.除法求值
查看>>
Leetcode C++《热题 Hot 100-51》152. 乘积最大子序列
查看>>
[Kick Start 2020] Round A 1.Allocation
查看>>
[Kick Start 2020] Round A 2.Plates
查看>>
Leetcode C++ 《第181场周赛-1》 5364. 按既定顺序创建目标数组
查看>>
Leetcode C++ 《第181场周赛-2》 1390. 四因数
查看>>
阿里云《云原生》公开课笔记 第一章 云原生启蒙
查看>>
阿里云《云原生》公开课笔记 第二章 容器基本概念
查看>>
阿里云《云原生》公开课笔记 第三章 kubernetes核心概念
查看>>
阿里云《云原生》公开课笔记 第四章 理解Pod和容器设计模式
查看>>
阿里云《云原生》公开课笔记 第五章 应用编排与管理
查看>>
阿里云《云原生》公开课笔记 第六章 应用编排与管理:Deployment
查看>>
阿里云《云原生》公开课笔记 第七章 应用编排与管理:Job和DaemonSet
查看>>
阿里云《云原生》公开课笔记 第八章 应用配置管理
查看>>