一.应用层注入(当我们在使用mybatis的时候,我们在使用什么?)
当我们在使用mybatis+spring的时候,我们实际上只在配置里面注入了两个类:
1.<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
2.<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer”>
顾名思义这两个类一个是用来产生sqlSession的sessionFactoryBean ,Factorybean 是Spring 为我们提供的一种bean扩展,工厂模式想必大家都很熟悉,把sessionFactory本身作为一个Spring的bean放到spring里面使用,这就是FactoryBean。这里我们在xml手动注入了一个路径:<propertyname="configLocation" value="classpath:mybatis/mybatis-configuration.xml”/>
和一个datasouce ,configlocation 就是我们mabatis配置文件的路径
还有一个是用来扫面mybatis配置文件的MapperScaner的Configurer ,这里面手动注入的只有一些配置项例如包名,SessionFeactoryBeanName ,真正的扫描工作由一个ClassPathMapperScanner 完成,在这个类里面我们手动配置了两个属性:
<property name="basePackage" value="com.vdian.feedcenter.admin.core.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>。
一个是dao层的包名,还一个就是之前说的sessionFactoryBean。
那么一下这两个类到底是什么?
先看 org.mybatis.spring.SqlSessionFactoryBean
先来看这个类的接口(由于java的单继承,当我们不能完全确认类与类之间的关系是一种is-a的关系的时候,往往是用接口来实现扩展,而继承则使用的比较谨慎,这一点在spring框架中表现的尤为明显)
1.FactoryBean
spring 给我们提供的一个扩展接口
用于管理各类BeanFactory 当我们在spring里面使用getBean(‘一个factoryBean的ID’)的时候,我们拿到的bean其实是factoryBean里面getObject()这个方法所返回的bean(factory),而不是factoryBean本身
2.InitiallizingBean
当spring遇到实现了这个接口的类,会在所有属性注入完成后调用一个用于初始化的方法:afterPropertiesSet(),我们一般把bean的初始化写在这个方法里面
3.ApplicationListenner
监听spring的spring的刷新事件,用于在spring刷新的时候更新类的内部信息
再来看这个类的方法:
除去一堆属性的get set 方法,我们需要关注的方法只有这么几个
1.afterPropertiesSet 对象的初始化方法:在确认了两个关键属性不为空之后,调用buildSqlSessionFactory 方法
2. buildSqlSessionFactory
顾名思义,这个方法会建造一个SqlSessionFactory 而且很可能使用可建造者模式。这个方法有一百多行,总体而言做了这么几件事情,
2.1.配置一个XMLConfigBuilder,这个builder里面有一个parse方法,里面就是解析mybatis配置文件的具体实现 ,parse方法返回一个Configuration 对象,这里面包含了我们在配置的xml里面写的所有东西。具体解析过程会在之后讲到,
2.2. XMLConfigBuilder 这个对象在构造函数里面会对 自己本身的Configuration 对象属性,进行一部分初始化工作,例如validate,但不会进行具体解析,在buildSqlSessionFactory 这个方法里面,我们拿到了这个初步初始化之后的Configuration 对象,进行了进一步的初始化,例如我们在spring里面配置的 插件 ,alias ,还有objectFactory ,transactionFactory
2.3.最后,调用XMLConfigBuilder 里面的parse方法,正式解析xml文件。
然后我们再看一下MapperScannerConfigurer
还是先看接口:
这里除了InitializingBean 之外还有两个aware 和 一个processor接口:
aware接口:感知接口,applicationContextAware :感知这个Bean所处的Spring的Context(上下文)
beanNameAware :感知这个Bean在Spring容器中的beanName
BeanDefinitionRegistryProcessor:
这里说一下postprocessor : spring 给我们预留的processor接口允许我们在spring 处理初始化逻辑的时候,允许程序员处理一些与spring框架本身初始化相关的事情,而不是Bean本身的初始化。
例如BeanDefinitionRegistryProcessor,这个接口就允许我门在初始化Bean本身(afterpropertiesset)之后,对spring框架本身所维护的BeanDefinitionRegistry做一些修改,至于BeanDefinitiionRegistry是什么 ,再之后的内容中会有解释。
然后再来看看方法:
postProcessBeanDefinitionRegistry:整个类里面只有这一个重要的方法 他是 实现了BeanDefinitionRegistryProcessor 接口必须实现的方法:它使得我门可以修改spring所维护的一个BeanDefinitionRegistry ,它是spring框架中非常重要的一个接口。
接口文档上是这么说的:
Interface for registries that hold bean definitions, for example RootBeanDefinitionand ChildBeanDefinition instances. Typically implemented by BeanFactories thatinternally work with the AbstractBeanDefinition hierarchy.
这个接口维护了一些个register(注册表?),这个注册表持有许多BeanDefinition(类的定义),例如根类与子类的定义,这个接口一般被抽象层级的BeanFactory所实现。
好像有点不知所云 ,我打个断点来看看:、
我们可以看到在实际上这个接口被DefaultListableBeanFactory实现,那这里的DefaultListableBeanFactory又是什么东西?
DefaultListableBeanFactory 是整个spring ioc的始祖 ,
当我们使用原生spring 我们会得到一个applicationContext,当我们想从Spring得到一个对象,我们会调用它的getBean 方法,那这个getBean是怎么实现的? 答案就是DefaultListableBeanFactory。
这个DefaultListableBeanFactory 就是我们所使用的所有的Spring Bean 的工厂类(也可以说是容器),它实现了BeanDefinitionRegistry这个接口。
mybatis在postProcessBeanDefinitionRegistry 方法里面拿到了BeanDefinitionRegistry
之后往里面插入了一些BeanDefinition ,先是new 了一个ClassPathMapperScanner 这个对象的作用是用来扫描base package(也就是我们doScan在MapperScannerConfigurer配置的basepackage属性包路径下所有的类和接口(一般是各种DAO),然后解析成BeanDefinition,然后插入BeanDefinitionRegistry 。
具体实现在ClassPathMapperScanner doScan()方法里面实现。 具体实现会在之后讲到。
二.核心层(mybatis是怎样工作的)
1.binding and mapper 绑定与映射 (当我们启动应用时,mybatis干了些什么)
mybatis框架的绑定与映射主要是将我们写的dao层的方法与配置文件xml中的sql语句相互映射。首先,他们是怎样被加载到我们的应用里面去的呢?
1.1 xml配置文件的解析
之前在介绍SqlSessionFactoryBean的时候就提到过xmlConfigBuilder 里面的parse方法是用来解析我们写的xml配置文件的,实际上在parse方法里面所调用XPathParser的把xml文件解析成一个XNode,的然后将这个XNode传入parseConfiguration 方法。 我们知道Xpath是w3c的一个标准,实际上在这个XPathParser 里面,也是调用的org.w3c.dom,和org.xml.sax 两个开源包来解析xml文件,具体实现这里略过,只看大概逻辑,如下:
平铺易懂的代码风格,每一行解析了一个子模块,所有的方法都没有返回值,解析结果统一储存在了这个对象的configuration,typeAliasRegistry ,typehandlerREgistey三个属性里面,最重要的mapper(类,方法的映射信息)被储存在configuration,来看一下configuration 这个属性对象,这是一个非常复杂的大对象(几十个方法与属性)。暂时只介绍mapper相关的方法与属性
1.方法:addMapper(Class<T> type)
parser从配置文件中解析出来一个接口,将其录入configuration
2.方法: T getMapper(Class<T> type, SqlSession sqlSession)
在spring 注入Bean的时候,需要通过这个方法得到一个实现了所需接口的Bean
3.属性:MapperRegistry mapperRegistry
configuration维护了一个MapperRegistry属性,addMapper 属性与getMapper 都是通过这个属性实现,来看一下MapperRegistry
两个属性:
Configuration config :Configration 与MapperRegistry 相互持有,在addMapper 的时候需要用到configration
Map<> knowmappers :map<接口,接口的工厂对象> 核心属性,维护了一个 接口 与 用于生产实现接口的 代理对象的 工厂对象。这里是一个典型的工厂模式
MapperProxyFactory如下
既然是工厂类,那么作用就只有一个,那就是产生对象,具体来讲就是产生实现了具体接口(比图XXXDAO)的代理对象。具体生产如下
首先会根据上层传过来的sqlSession生成一个MapperProxy ,这个MapperProxy 实现了InvocationHandler ,java.lang.reflect 这个包下面有一个Proxy类与一个InvocationHandler 接口,调用Proxy的newProxyInstance 可以得到一个指定接口的代理类,接口里面的方法由InvocationHandler 拦截,调用InvocationHandler 里面的invoke
,使用反射(invoke)的方式调用方法的具体实现,在MapperProxy这里mybatis框架做了一层方法的缓存:
至此,一个实现了指定接口(DAO)的Instance 就被生成。
那么mybatis是怎样将这个Instance注入到我们所写的业务Bean里面去的呢?
回到之前说的ClassPathMapperScanner ,我们看一下它的doScan方法
首先这个函数调用了super.doScan() ClassPathMapperScanner 继承自ClassPathBeanDefinitionScanner 这个类是Spring 里面默认的ClassPathScaner spring用它扫描目的路径(包)里面所有的带Component,Service,Controller注解的class文件,并解析成相应BeanDefinition ,这个类的doScan返回了一个Set<BeanDefinitionHolder>,这就是扫描结果。
在ClassPathMapperScanner 里面的doScan拿到扫描结果BeanDefinition之后,干了两件事情:
首先为BeanDefinition设置 了四个属性 addToConfig,markerInterface,sqlSessionTemplate,sqlSessionFactory (之后会用到),然后修改BeanDefinition 的BeanClass,改为MapperFactoryBean,这才是将MapperProxy注入业务代码的关键部分,当Spring 注入一个Instance时,会根据BeanClass调用ClassLoader 将Class加载到系统,所以实际上我们注入一个DAO的时候,会注入一个MapperFactoryBean(然而实际上我们调用的是MapperProxy),我们来看一下MapperfactoryBean
这个类继承自SqlSessionDaoSupport 同时实现了FactoryBean ,
FactoryBean之前说过,在spring里面将注入getObject里面所返回的对象,直接看getObject方法即可。
之前说过在 BeanDefinition里面设置了一个属性sqlSessionTemplate ,这里在getSqlSession里面被拿出来了,然后调用了getMapper方法。
而getMapper方法是调用了configuration里面的getMapper方法,而这一个configuration就是在本节开头提到的configuration 属性,它最开始出现在SqlSessionFactory里面,而SqlSessionFactory是一个单列,他里面这个属性被很多对象引用。而这里所返回的Mapper,就是之前所说过的MapperProxyFactory 生产的MapperProxy。自此,当我们启动应用的时候,mybatis所做的初始化工作就完成了。
2.invoke and excute 反射与执行 (当我们的业务调用DAO层的时候,mybatis干了什么)
我们业务代码里面所注入的DAO层的Bean实际上是一个MapperProxy,它代理了所有的Dao层的方法,我们来看一下mapperProxy的invoke方法
这里可以看到先对上层传入的方法做了判断,如果DeclaringClass(声明这个方法的类)是Object ,就调用本类的方法,如果不是,就去Proxy里面维护的一个methodCache 里面拿一个MapperMethod 类,没有的的话就新建一个(总而言之就是一个缓存),然后调用MapperMethod MapperMethod里面的excute方法。来看一下MapperMethod 类:这个类没有实现任何接口,父类是Object ,所以先看属性和构造方法
这里定义了两个final属性,在构造函数里面也主要是构造这两个final属性。先看SqlCommand这个类,它是类MapperMethod的内部类
顾名思义,sqlCommand是一个sql命令的封装,但我们的sql是写在xml配置文件里面的所以这个类的构造函数干了这么几件事:首先通过传入的Method找到sqlStatement的名字(接口名.方法名),去configration里面找对应方法的statement(所有mapper的xml配置文件里面的内容都被解析到了configration里面),然后处理Statement的继承关系,最后将Statement的id 和type记录在对象的属性里面,供以后调用Statement。
然后再看一下MethodSignature ,这个类也是一个内部类
从构造函数看出,这里面储存了对于DAO接口所定义的方法的一些描述,例如返回值 类型 参数数量之类
最后看一下最重要的excute方法:
在sqlcommand里面初始化了一个command type,描述了
sql语句的类型,整理根据sql的类型(增删改查)和返回值调用了sqlSession里面的insert,delete,update,select 方法,这里的sqlSession是一个接口,mybatis实际上默认是用SqlSessionTemplate 实现了这个接口(在mapperProxyFactory里面),来看一下增删改查的具体实现类SqlSessionTemplate:
他有四个 final属性:
其中sqlSessionProxy非常重要,因为所有增删改查都是通过调用它实现的,以selectOne为例:
既然sqlSqssionTemplate本身实现了sqlSession 接口,为什么要加一层sqlSessionProxy 呢?来看sqlSessionProxy的拦截器里面:
答案是事务管理,在invoke里面将不同的dao层的方法路由到不同的sqlSession对象,方便事务的回滚,所以需要一个代理,这里调用了SqlSessionUtils.getSqlSession,里面根据上层传入的executorType与Spring自带的TransactionManager 返回正确的SqlSession。然后再调用session的增删改查。这里默认的sqlSession实现类是DefaultSqlSession(由DefaultSqlSessionFactory生成,这个工厂类就是我们在xml配置的SqlSessionFactoryBean 生成的 ),来看看这个类
这个才是mybatis增删改查数据看的SqlSession的具体实现,以select为例:
参数解释:statement: 调用的包名+类名+方法名
parameter:DAO层的参数
roundBounds:选择边界
这个方法首先通过在configuration里面拿到statement对应的MappedStatement,MappedStatement 就是我们为每个dao的接口的方法写的一个xml文件下的内容(例如sql语句 返回类型的映射),在初始化应用的时候这些xml被解析,放入configuration ,然后把MappedStatement和DAO层参数传入executor(mybatis里面默认使用CachingExecutor,在sessionFactory里面设置),调用query方法执行。现在我们深入CachingExecutor的query方法:
既然名字叫CachingExecutor,那在查询的时候肯定有一层缓存,这个缓存是内存缓存,独立的存在于每个MappedStatement,在缓存不命中的情况会调用delegate属性(一个SimpleExecutor)的query方法:
我们可以看到这里又有一层内存级别cache ,这层cache独立存在于Executor(同一批次query共用此chache,查询完毕后马上清除)在不命中的情况下才会调用queryFromDataBase。
在queryFromDataBase里面,调用doQuery查询数据库,并且将结果存入localcache,SimpleExecutor重写了doQuery方法:
这将MappedStatement转换成一个RoutingStatementHandler 然后调用了里面的query方法,这个Handler的主要作用是根据Statement的类型调用不同的StatementHandler代理:
这里的statement是PREPARED类型的。所以我们得到的是一个PreparedStatementhandler。
在doQuery里面,在调用handler执行Statement之前,还会prepareStatement
这里会调用PrepareStatementhandler根据Connection的类型对Statement进行预处理这里的Connection与我们所使用的DataSource相关,例如我们这里使用的是一个DistributedConnection ,那么这里预处理之后的Statement就是一个DistributedPreparedStatement。反之,如果这里用的是mysql,那就回返回一个mysql包下面的Statement。
在handler与statement都准备完毕之后,就会执行query了:
先将statement强转为preparedStatement ,然后调用statement里面的execute方法(与数据库的具体类型相关),最后处理结果集。与数据库类型相关的部分略过,这里看一下结果集的处理部分:DefaultResultSetHandler,实现了ResultHandler接口:
在这里我们关注handleResultsSets方法的实现。
构造函数如下:
可以看到这类可以拿到configuration (全局单例),executor (全局单例)和mappedStatement等等对象,
结果集处理逻辑如下:
首先拿到mappedStatement里面的result maps,那里面储存了我们在xml配置文件里面定义的Dao层方法的返回集的类名,遍历结果多个结果集,根据每一个结果集的resultmap将每一个结果集转化为xml配置的结果对象。单个结果集转换逻辑如下:
这里会先处理结果集映射的父类映射,然后调用handleRowValues 处理多行结果:
每一行结果都会分两种处理方式:关联处理和简单处理,关联处理表示本次查询拿到的结果集可能从多个表中读取,需要解析它们之间的关系并整合成一个完整对象,简单处理表示查询结果从一个表或者视图中得到,只需要进行基本类型(元数据)的映射。
这里我们看一下简单处理:
做了四件事情:1.新建一个resultContext
2.去除没用的行
3.遍历所有行,每行生成一个rowValue,
4.存储结果(联合查询:将结果与其父结果链接 非联合查询:直接返回结果 )
然后看一下每一行的结果是如何生成的:
首先根据resultMap 与resultset等,生成了一个空的resultObject,这个resultObject的类型就是Dao层接口所定义的返回值类型,DefaultResultSetHandler 本身持有一个objectFactory,这个objectFactory可以根据ClassType调用类的构造函数产生新建对象,resultMap就存有result的ClassType。
得到空的resultObject之后,将其包装成一个metaObject,然后对MetaObject
进行属性值的映射:
这里遍历了resultMap(我们配置在xml文件里面的结果集映射关系),从resultSet中取出相应的值,并转化成对象。调用metaObject的setValue 将属性的值设置到结果对象内。
至此,一个结果对象装配完成,之后将返回上层业务代码进行处理
三.总结
mybatis初始化流程如下:
mybatis执行期间流程如下:
相关推荐
《Spring MVC MYBatis企业应用实战+源码》是一份深度探讨如何在企业环境中整合并高效使用Spring MVC和MyBatis两大主流Java框架的资源包。这个资源包含了一本PDF电子书《spring+mybatis企业应用实战》以及配套的源...
本文将重点讲解MyBatis操作入门的相关知识,通过源码解析,帮助你深入理解MyBatis的工作原理及使用方法。 1. MyBatis概述 MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了...
《Spring+Mybatis企业应用实战项目源码解析》 在当今的Java开发领域,Spring框架与Mybatis的组合是广泛采用的技术栈,尤其在企业级应用中占据主导地位。本项目源码结合了这两个强大的工具,旨在提供一个实战性的...
《Java EE企业级应用开发教程Spring+Spring MVC+MyBatis》是一本深入探讨Java企业级应用程序开发的书籍,源代码包含多个章节的实例,旨在帮助读者理解和掌握使用Spring、Spring MVC和MyBatis框架进行实际开发的关键...
- 学习Spring MVC和MyBatis的整合,可以参考官方文档、教程和开源项目的源码。 - 通过调试源码,理解其实现原理,有助于提升实战技能。 总结,这个压缩包提供了一个完整的Spring MVC和MyBatis整合的示例,包含了...
在本项目"spring-boot-mybatis-cache-thymeleaf学习练习demo源码"中,我们可以深入学习和实践如何将Spring Boot、MyBatis、Cache(通常指的是Spring Cache)以及Thymeleaf这四个关键组件整合在一起,创建一个高效、...
五、项目结构与源码解析 压缩包中的"springboot-vue-CASMS-main"目录包含了整个项目的源码。其中,"src/main/java"存放后端代码,包括Springboot的配置、控制器、服务及实体类等;"src/main/resources"配置文件和...
在深入探讨MyBatis实战源码之前,我们先要理解MyBatis的基本概念和架构。 1. MyBatis概述 MyBatis消除了几乎所有的JDBC代码和手动设置参数以及获取结果集的工作。MyBatis可以使用简单的XML或注解来配置和原始映射,...
SSM框架是Java Web开发中常用的一种组合,由Spring、SpringMVC和Mybatis三大组件构成。这个框架整合为开发者提供了高效、灵活的开发环境,适用于构建复杂的企业级应用。 **1. Spring框架** Spring是Java领域的一个...
《Spring+MyBatis企业应用实战》是一本深入解析如何在实际项目中集成并使用Spring和MyBatis框架的书籍。这本书旨在帮助开发者熟练掌握这两款流行的技术,并将其有效地应用于企业的软件开发中。Spring作为一款全面的...
SSM框架是Java Web开发中常用的三大框架集成,即Spring、SpringMVC和Mybatis的组合,它们各自负责不同的职责,协同工作以构建强大的企业级应用。Maven作为项目管理和构建工具,使得依赖管理和构建流程更为简洁。在...
《SpringBoot+Vue3知识库系统源码解析》 在当今的互联网开发中,Spring Boot 和 Vue.js 是两个非常流行的框架。Spring Boot 提供了快速构建后端服务的能力,而 Vue.js 则是用于构建前端用户界面的强大工具。本文将...
本项目“Java-SSM+Vue 后勤管理系统”正是这样一款综合运用了Java、Spring、SpringMVC和MyBatis(SSM框架)以及前端Vue.js技术的实战型项目,旨在解决校园后勤管理中的各种问题,如物资管理、维修申请、环境维护等。...
在本实践项目中,我们探讨的是基于Spring和MyBatis框架构建的企业级人事管理系统。SSM框架,即Spring、SpringMVC和MyBatis的组合,是Java Web开发中的常见选择,尤其适用于需要灵活数据库操作和强大业务逻辑处理的...
《互联网轻量级SSM框架解密:Spring、Spring MVC、MyBatis源码深度剖析》Spring 源码剖析篇基于Spring 4.3.2 版本,剖析了Spring 上下文、Spring AOP 和Spring 事务的实现,并通过实例展示了框架陷阱的隐蔽性及学习...
《Spring+Mybatis 企业应用实战源码》涵盖了从第2章到第10章的实战内容,这是一份深入探讨Spring与Mybatis整合的宝贵资料。Spring是一个强大的Java应用程序框架,而Mybatis则是一个优秀的持久层框架,两者结合可以...
SSM框架整合是Java开发中常见的技术栈,它结合了Spring、Spring MVC和Mybatis三个强大的框架,以实现高效、灵活的Web应用开发。本文将详细介绍SSM框架整合的步骤,帮助开发者理解并掌握这一技术。 一、Spring框架 ...
- `SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)SSM整合例子-源码`:这部分应该是实际的项目源代码,包括了Spring的ApplicationContext配置、SpringMVC的DispatcherServlet配置、MyBatis的Mapper接口和XML...
这个项目源码提供了一个实战学习平台,帮助开发者理解和掌握Spring MVC与MyBatis的整合应用,同时可以了解到如何在实际项目中组织代码结构、配置文件以及进行测试。通过深入研究这些代码,你将能够更好地运用这两个...
《Spring与MyBatis整合详解》 在Java开发领域,Spring和MyBatis是两个非常重要的框架,它们分别在依赖注入和数据访问层...希望这份关于"Spring MyBatis整合"的详细解析和源码分析,能为初级学者提供有力的学习支持。