mybatis启动流程

1、首先来看看最简单的mybatis项目启动过程

public static void mybatisTest() throws IOException {
String resource = "mybatis/mybatis-config.xml";
//配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = null;
try {
//开启一个session
sqlSession = sqlSessionFactory.openSession();
//获取mapper
JobConfigDao jobConfigDao = sqlSession.getMapper(JobConfigDao.class);
//查询数据
List<JobConfig> jobConfigList = jobConfigDao.listConfig();
System.out.println(jobConfigList);
} finally {
if (sqlSession != null)
sqlSession.close();
}
}

这个过程主要是SqlSessionFactory 的创建,先获取配置信息,然后通过SqlSessionFactoryBuilder来创建SqlSessionFactory 。

build方法的核心代码是:

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());

可以看到mybatis是通过XMLConfigBuilder来解析配置文件的。来看看XMLConfigBuilder的parse方法:

public Configuration parse() {
//XMLConfigBuilder中有一个boolean类型parsed,来标记是否解析过配置文件,解析过之后就设置为true,防止同样的配置被重复解析
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
// 解析configuration节点下的配置
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
} private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
// 解析mapper
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}

可以看到,在parseConfiguration方法中会将mybatis-config.xml配置下的各种属性获取出来一一解析,并映射到相关的属性上面去。

mybatis定义的接口,怎么找到实现的?

通过上面的启动过程,已经知道mybatis是通过XMLConfigBuilder来解析配置文件的,具体解析是在parseConfiguration方法的中,获取到mappers配置节点,最后一行this.mapperElement(root.evalNode("mappers"))将节点信息转交给XMLMapperBuilder来完成对mapper的解析。

来看看XMLMapperBuilder的parse方法:

public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
//通过命名空间绑定mapper
this.bindMapperForNamespace();
} this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}

因为这里的重点是想知道定义的接口是怎么实现的,所以直接来看看bindMapperForNamespace方法对如何来实现mapper的绑定:

private void bindMapperForNamespace() {
String namespace = this.builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class boundType = null; try {
//通过反射获取类类型
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException var4) {
;
} //如果当前类还没有绑定到配置的缓存中
if (boundType != null && !this.configuration.hasMapper(boundType)) {
this.configuration.addLoadedResource("namespace:" + namespace);
//通过mybatis的Configuration将mapper注册到MapperRegistry
this.configuration.addMapper(boundType);
}
}
}

来看看MapperRegistry 的addMapper:

public <T> void addMapper(Class<T> type) {
this.mapperRegistry.addMapper(type);
} public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {//先判断是否为接口
if (this.hasMapper(type)) {//如果已经注册,抛出异常
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//创建一个代理MapperProxyFactory 并将该mapper缓存到内存的Map中去
this.knownMappers.put(type, new MapperProxyFactory(type));
//绑定注解
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
} }
}
}

通过对上面源码的跟踪分析,终于知道,mybatis通过MapperProxyFactory为每个接口都提供了一个代理实现,然后通过MapperRegistry将mapper注册到容器中的。

那么在使用的时候又是如何来获取这个mapper的呢?

答案是:反射加上代理

首先通过SqlSession的getMapper方法,mybatis为SqlSession提供了一个默认的实现DefaultSqlSession,DefaultSqlSession的内部属性如下:

//Configuration配置
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;

DefaultSqlSession获取配置,然后配置中获取MapperRegistry,从MapperRegistry中获取到mapper代理工厂,再通过MapperProxyFactory的newInstance来创建mapper代理类MapperProxy

总结:对于Mybatis,记住几个关键的处理类:SqlSessionFactoryBuilder创建SqlSessionFactory,SqlSessionFactory中开启一个SqlSession,通过sqlSession来执行用户操作;RegisterMapper将mapper注册到容器中,通过MapperProxyFactory来创建mapper代理,mapper代理最终是通过jdk反射包中Proxy代理类来创建的。

启动时创建的SqlSessionFactory是DefaultSqlSessionFactory,DefaultSqlSessionFactory中拥有属性Configuration,Configuration是通过XMLConfigBuilder从配置文件中解析出来的各种配置信息,Configuration中有MapperRegistry,MapperRegistry中用一个final类型的Map存储了用户定义Mapper代理实现工厂MapperProxyFactory,Mapper代理实现是MapperProxy。

最新文章

  1. ElasticSearch+Kibana 索引操作( 附源码)
  2. Apache POI 实现对 Excel 文件读写
  3. android studio小技巧
  4. 【markdown】markdown常用语法
  5. Educational Codeforces Round 3 E. Minimum spanning tree for each edge (最小生成树+树链剖分)
  6. [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)
  7. ios的@property属性和@synthesize属性
  8. Unity GUI编程
  9. POJ3658Matrix( 双重二分+负数+死循环)
  10. outlook 2003配置连接exchange server 2010报错——无法完成此操作。 与 Microsoft Exchange Server 的连接不可用。 Outlook 必须联机或连接才可完成该操作
  11. shell中exec解析(转)
  12. leetcode[89] Merge Sorted Array
  13. Pyjwt ,python jwt ,jwt
  14. Notes : &lt;Hands-on ML with Sklearn &amp; TF&gt; Chapter 4
  15. saltstack grains
  16. mysql 开发进阶篇系列 26 数据库RPM安装演示
  17. U3D Time类
  18. tomcat使用spring-loaded实现应用热部署
  19. Raft协议学习笔记
  20. golang 学习笔记 ---内存分配与管理

热门文章

  1. 转。Nas配置。想找原版没找到,全是转载的,也没注出处,无语。
  2. vue组件实例的生命周期
  3. webpack 添加eslint代码审查
  4. centos 安装 ImageMagick
  5. 2018——2019 20165239Exp9 Web安全基础
  6. CSS:CSS 属性 选择器
  7. dom读写xml
  8. 关于Unity中文件读取
  9. 洛谷 P4196 [CQOI2006]凸多边形 (半平面交)
  10. Java原始数据类型