Spring源码深度解析笔记(5)——整合MyBatis

MyBatis本事Apache的一个开源项目iBatis,2010年这个项目由Apache Software Foundation迁移到Google Code,并改名为MyBatis

MyBatis是普通的SQL查询、存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数手工设置以及结果集的检索,MyBatis使用简单的XML或者注解用于配置和原始映射,将接口和JAVA的POJOs映射成数据库中的记录

9.1 MyBatis的独立使用

  1. 建立PO
  2. 建立Mapper,数据库操作的映射文件,也就是常说的DAO,用于映射数据库的操作,可以通过配置文件指定的方法对应SQL语句或者直接使用Java提供的注解的方式进行SQL的指定
  3. 建立配置文件,配置文件的主要用于配置程序中可变性高的设置,一个偏大的程序一定会存在一些经常会变的变量,如果每次变化都需要改变源码那会是非常糟糕的设计,所以,看到各种各样的框架或者应用的时候都避免不了要配置配置文件,MyBatis中的配置文件主要封装在configuration中
  4. 建立映射文件,对于MyBatis全局配置中的mappers的配置属性,主要用于建立对应数据库操作的接口的SQL映射。MyBatis在这里会将这里设定的SQL对应的Java接口关联起来,以保证MyBatis中调用接口的时候回到数据库中执行相应的SQL来简化开发。

9.2 Spring整合MyBatis

了解了MyBatis的独立使用过程以后,Spring对于整合MyBatis步骤如下:

  1. Spring配置文件,配置文件是Spring的核心,Spring的所有操作也都是由配置文件开始的。对比之前独立使用MyBatis的配置文件,之前在environment中设置的dataSource被转移到了Spring的核心配置文件中管理,而且,针对MyBatis,注册了SqlSessionFactoryBean类型bean,以及映射接口的MapperFactoryBean类型的bean。MyBatis提供的配置文件包含诸多属性,虽然大多数情况我们会保持MyBatis原有的风格,将MyBatis的配置文件独立出来,并在Spring中的SqlSessionFactoryBean类型的bean中通过configLocation属性引入,但是,并不代表Spring不支持直接配置。
  2. MyBatis配置文件
  3. 映射文件

至此,已经完成了Spring与MyBatis的整合,对于MyBatis方面的配置文件,除了dataSource配置移至Spring配置文件中管理外,并没有变化,而在Spring的配置文件中又增加了用于处理MyBatis的两个bean。

9.3 源码分析

通过Spring整合MyBatis的实例,通过分析整合实例中的配置文件,可以知道配置的bean是成树状结构的,而在树的最顶层的类型是SqlSessionFactoryBean的bean,它将其他的bean组装在一起了。

9.3.1 sqlSessionFactory创建

通过配置文件分析,对于配置文件的读取解析,Spring应该通过sqlSessionFactory封装MyBatis中的实现。

  1. InitializingBean:实现此接口的bean会在初始化时调用afterPropertiesSet方法来进行bean的逻辑初始化
  2. FactoryBean:一个某个bean实现此接口,那么通过getBean方法获取bean是其实是获取此类的getObject()返回的实例。
  1. sqlSessionFactoryBean的初始化
buildSqlSessionFactory();

此函数主要目的就是对于sqlSessionFactory的初始化,通过之前展示独立使用MyBatis的示例,sqlSessionFactory是所有MyBatis功能的基础。

  1. 获取sqlSessionFactoryBean实例,由于sqlSessionFactoryBean实现了FactoryBean接口,所以当通过getBean方法获取对应实例时,其实是获取该类的getObject()函数返回的实例,也就是获取初始化后sqlSessionFactory属性。

9.3.2 MapperFactoryBean的创建

为了使用MyBatis功能,实例中的Spring配置文件提供了两个bean,除了之前分析的sqlSessionFactoryBean类型的bean以外,还有一个MapperFactoryBean类型的bean。

  1. MapperFactoryBean的初始化,因为实现了InitializingBean接口,Spring会保证在bean初始化首先调用afterPropertiesSet方法来完成初始化逻辑,追踪父类,发现afterPropertiesSet方法是在DaoSupport类中实现。
checkDaoConfig();
initDao();

从函数名称开看大致推测,MapperFactoryBean的初始化包括对DAO配置的验证以及对DAO的初始化工作,其中initDao()方法是模板方法,设计为了留个子类做进一步逻辑处理,而checkDaoConfig()才使我们分析的重点。

结合代码了解对于DAO配置的验证,Spring做了一下几个方面的工作。

  1. 父类中对于sqlSession不为空的验证,sqlSession作为根据接口创建映射器代理的接触类一定不可以为空,而sqlSession的初始化工作是在设定sqlSessionFactory属性时完成的。
  2. 映射接口的验证,接口是映射器的基础,sqlSession会根据接口动态创建相应的代理类,所以接口是必不可少的。
  3. 映射文件存在性验证,对于函数前半部分的验证都很容易理解,无非是对配置文件的属性是否存在做验证,但是后面部分是完成了什么验证呢?在MyBatis实现过程中并没有手动调用configuration.addMapper方法,而是在映射文件读取过程中一旦解析到如< mapper namespace = “xxx” >,便会自动进行类型映射的注册。在上面函数中,configuration.addMapper其实是将mapper注册到映射类型中,如果可以保证这个接口一定存在对应的映射文件,那么其实这个验证并没有必要。但是由于这个是我们自行决定的配置,无法保证这个配置的接口一定存在对应的映射文件,所以这里非常有必要进行验证。在执行此代码的时候,MyBatis会检查嵌入的映射接口是否存在对应的映射文件,如果没有抛出异常,Spring正是在用这种方式来完成接口对应的映射文件存在性验证。
  1. 获取MapperFactoryBean实例,由于MapperFactoryBean实现了FactoryBean接口,所以通过getBean方法来获取对应实例的时候其实是获取该类的getObject()函数返回的实例。
9.3.3 MapperScannerConfigurer

我们在applicationContext.xml中配置了mapper供需要时使用。但是如果需要用到的映射器较多的话,采用这种配置方式就显得很低效。为了解决这个问题,可以使用MapperScannerConfigurer,让他扫描特定的包,自动成批的创建映射器。从而大大减少配置工作量。

在配置文件中增加MapperScannerConfigurer的配置,basePackage属性是为映射器接口文件设置基本的包路径。可以使用分号或逗号作为分隔符设置多于一个的包路径。每个映射器将会在指定的包路径中递归的被搜索到。被发现的映射器将会使用Spring对自动侦测组件默认的命名策略来命名。也就是说,如果没有发现注解,它会使用映射器的非大写的非完全限定类名。但是如果发现@Component或JSR-330@Name注解,它会获取名称。

通过上面的配置,Spring会帮助我们对指定包的所有接口进行自动注入,而不需要为每个接口重复在Spring配置文件中进行生命了。首先查看类的层次结构,MapperScannerConfigurer实现了InitializingBean接口的afterPropertiesSet方法,但是afterPropertiesSet方法除了对basePackage属性的验证代码外并没有太对的逻辑实现,所以,MapperScannerConfigurer又实现了BeanDefinitionRegistryPostProcessor和BenaFactoryPostProcessors接口,Spring在初始化的过程中同样会保证这两个接口的调用。

查看MapperScannerConfigurer对BeanDefinitionRegistryPostProcessor的实现。

processPropertyPlaceHolders();

大致看一下代码实现,正式完成了对指定路径扫描的逻辑

  1. processPropertyPlaceHolders属性的处理,首先难题就是processPropertyPlaceHolders属性的处理,只能通过processPropertyPlaceHolders()方法来反推属性所代表的功能。BeanDefinitionRegistrys会在启动的时候调用,并会早于BenaFactoryPostProcessors的调用,这就意味着PropertyResourceConfigurers还没有被加载所有对属性文件的引用将会失效。为避免此种情况的发生,此方法手动的找出定义的PropertyResourceConfigurers并进行提前调用以保证对于属性的引用可也正常工作。
    通过processPropertyPlaceHolders属性的配置,将程序引入processPropertyPlaceHolders函数中来完成属性文件的加载。至此主语理清了这个属性的作用。
  1. 找到所有已经注册的PropertyResourceConfigurer类型的bean。
  2. 模拟Spring中的环境来用处理器。治理通过DefaultListableBeanFactory()来模拟Spring中的环境(完成处理器的调用后便失效),将映射的bean,也就是MapperScannerConfigurer类型的bean注册到环境中来进行处理器的调用,处理器propertyPlaceholderConfigurer调用完成的功能,即找到所有bean中应用属性文件的变量并替换。也就是说,在处理器调用后,模拟环境中模拟的MapperScannerConfigurer类型的bean如果有引入属性文件中属性那么已经被替换,这时,再将模拟bean中的相关属性提取出来应用在真实的bean中。
  1. 根据配置属性生成过滤器,在postProcessBeanDefinitionRegistry方法中可以看到,配置中支持很多属性的设定,属性设置后通过在scanner.registryFilters()代码生成对应的过滤器控制扫描的结果。
// 对于annotationClass属性的处理
// 对于markerInterface属性的处理
// 不扫描package-info.java文件

代码中得知,根据之前属性的配置生成了对应的过滤器。

  1. annotationClass属性的处理,如果annotationClass不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果。而封装此属性的过滤器是AnnotationTypeFilter。AnnotationTypeFilter保证在扫描对应Java文件只接受标记有注解为annotationClass的接口。
  2. markerInterface属性的处理,如果markerInterface不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果。而封装此属性的过滤器就是实现AssignableTypeFilter接口的局部类,标识扫描过程中只有实现markerInterface接口的接口才会被接受。
  3. 全局默认处理,再上面两个属性中如果存在其中任何属性。acceptAllInterface的值都会改变,但是如果用户没有设置以上两个属性,那么Spring会增加一个默认的过滤器实现TypeFilter接口的局部类,旨在接受所有接口文件。
  4. package-info.java的处理,对命名为package-info的Java文件,默认不作为逻辑实现接口,将其排除掉,使用TypeFilter接口的局部类是想match方法

控制扫描文件Spring是通过不同的过滤器完成,这些定义的过滤器记录在includeFilters和excludeFilters属性中。

  1. 设置了相关属性以及生成了对应的过滤器后便可以进行文件扫描了,扫描工作是有ClassMapperScanner类型的scanner中的scan方法完成的。
    scan是个全局方法,扫描工作是通过doScan(basePackages)委托给了doScan方法,同时还包括includeAnnotationConfig属性处理,AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)代码主要完成对注解处理器的简单注册。
// 扫描basePackage路径下的java文件
findCandidateComponents(basePackage)
// 解析scope属性
// 如果是AnnotatedBeanDefinition类型的bean,需要检测下常用的注解
// 检测当前bean时候已注册
// 如果当前bean是用于生成代理的bean那么需要进一步处理

findCandidateComponents方法根据传入包的路径信息结合类文件路径拼接成文件的绝对路径,同时完成了文件扫描过程并且根据对应文件生成了对应的bean,使用ScannedGenericBeanDefinition类型的bean承载信息,bean中只记录了resource和source信息,其中isCandidateComponent(metadataReader)。此代码用于判断当前扫描的文件是否符合要求,之前的一些过滤器信息也这是在此时派上用场。至此加入了之前过滤器的两个属性includeFilters和excludeFilters,并知道对应的文件是否符要求是根据过滤器中match方法返回的信息来判断的。当然用户可以实现并注册满足自己业务逻辑的过滤器来控制扫描的结果,metadataReader中有你过滤所需要的全部文件信息。至此完成了文件的扫描过程的分析。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring整合Mybatis源码分析可以分为以下几个步骤: 1. 创建Spring容器并加载配置文件。在Spring Boot中,可以通过@SpringBootApplication注解来创建Spring容器,并在配置文件中配置Mybatis相关的属性。 2. 创建Mybatis的SqlSessionFactory。Spring Boot会自动配置Mybatis的SqlSessionFactory,通过读取配置文件中的数据源信息和Mybatis的配置信息,创建SqlSessionFactory对象。 3. 注册Mybatis的Mapper接口。Spring Boot会自动扫描项目中的Mapper接口,并将其注册到Spring容器中。 4. 创建Mapper代理对象。Spring Boot使用Mybatis的MapperFactoryBean来创建Mapper接口的代理对象。在创建代理对象时,会使用SqlSessionFactory来创建SqlSession,并将SqlSession注入到Mapper接口中。 5. 使用Mapper代理对象进行数据库操作。通过调用Mapper接口的方法,可以实现对数据库的增删改查操作。 整个过程中,Spring Boot通过自动配置和注解扫描的方式,简化了Spring整合Mybatis的配置和使用过程,使得开发者可以更方便地使用Mybatis进行数据库操作。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [SpringBoot整合Mybatis源码解析](https://blog.csdn.net/u013521882/article/details/120624374)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring-Mybatis整合源码分析](https://blog.csdn.net/qq_42651904/article/details/111059652)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Spring源码解析整合Mybatis](https://blog.csdn.net/heroqiang/article/details/79135500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值