mybatis随笔一之SqlSessionFactoryBuilder
SqlSessionFactoryBuilder是构建sqlSessionFactory的入口类

从该类的方法可知,它是通过不同的入参来构造SqlSessionFactory,除了最后一个configuration入参方法外,其余方法最终都调用如下方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
如上图所示,先创建了一个XMLConfigBuilder类的实例
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
在这个初始化过程中,首先创建了XMLMapperEntityResolver类的实例,这个类顾名思义是个实体mapper文件实体解析器,里面有个map将mybatis的xml文件与对应的解析文件关系保存起来,初始化后再实例化XPathParser
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
commonConstructor比较简单,就是给一些类变量赋值,并且初始化了一个xpath对象,这里使用了工厂设计模式。
new InputSource(inputStream)就是将输入流赋予自己内部的一个变量byteStream。
createDocument(new InputSource(inputStream))这里面主要做了这几件事通过DocumentBuilderFactory生成DocumentBuilder,并将entityResolver赋给它的属性字段同时定义了一个处理异常的内部类,
然后通过builder.parse(inputSource)将输入流解析成一个document对象。
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
new Configuration()主要是在构造方法里面注册了别名
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
registerAlias是TypeAliasRegistry类的方法,将别名与类的关系保存在了内部的private final Map<String, Class<?>> TYPE_ALIASES
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
super(new Configuration())就是将相应的变量保存在父类属性上,方便其它子类使用。
ErrorContext.instance()方法内部采用的threadlocal方式,每个线程都持有一个ErrorContext对象resource("SQL Mapper Configuration")就是一个简单的赋值给resource变量方法。
至此一个XMLConfigBuilder对象就创建出来了,然后就进入了build(parser.parse())。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parser.evalNode("/configuration")在初始化过程中已经将mybatis_config.xml文件解析成document对象,这里使用xpath解析工具将configuration节点解析成一个XNode对象。
private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
该方法一看便知是扫描configuration节点下的诸如properties、typeAliases节点做相应的处理。稍微分析其中几个方法
propertiesElement(root.evalNode("properties"))是读取properties节点下的property子元素以及resource指向的资源文件路径,将两者合并为一个properties并保存到内部的vars变量。
typeAliasesElement(root.evalNode("typeAliases"))读取子元素时首先判断是否package,是的话扫描包下的非接口、匿名、内部类注册别名。
pluginElement(root.evalNode("plugins"))将插件加到插件链上。
重点提一下最后的mapperElement方法,这个方法内首先判断子元素是不是package,若是<mapper resource="mapper/demo.xml"/>这种形式的。其中最重要的是下面两行代码
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
构建XMLMapperBuilder对象前面已经分析过了,重点分析parse方法
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
该方法获取mapper文件的namespace来实例化mapper接口,这也就解释了为什么namespace要和mapper接口全路径一致。
其中值得注意的是configuration.addMapper(boundType)方法,该方法调用了mapperRegistry.addMapper(type)方法。
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperProxyFactory是个代理接口方法工厂,后面会使用到它,就是通过它来给没有实现类的mapper接口代理。
重点看其中的parser.parse()方法
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
先得到接口里的methods,然后遍历解析方法,查看该方法有没有注释sql语句有的话拼接这些语句,因为现在流行使用xml文件配置方式更加灵活的处理sql语句,因此这里跳过。
parsePendingMethods方法是有些insert或者update的select语句引入了sql片段,但是sql片段还没解析到,因此先将这些方法pend,后面每有新的mapper解析时都会尝试解析完这些pend方法。
同理parsePendingResultMaps(),parsePendingChacheRefs(),parsePendingStatements()三个方法也类似。
现在configuration已经基本解析完成,然后调用
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
返回一个默认的SqlSessionFactory实现,其内部持有一个Configuration变量。
至此SqlSessionFactoryBuilder创建DefaultSqlSessionFactory的过程完成。
mybatis随笔一之SqlSessionFactoryBuilder的更多相关文章
- mybatis随笔三之SqlSession
在上一篇文章我们已经得到了DefaultSqlSession,接下来我们对sqlSession.getMapper(DemoMapper.class)这种语句进行分析 @Override public ...
- mybatis随笔二之SqlSessionFactory
在上一篇文章我们已经得到了DefaultSqlSessionFactory @Override public SqlSession openSession() { return openSession ...
- MyBatis随笔
前一阵参与了一个项目的搭建,为了快速开发再加上学一些新东西,准备采用React+Spring MVC+MyBatis的架构. 花了一些时间最终把Spring MVC+MyBatis打通. 这里总结下M ...
- mybatis随笔四之MapperProxy
在上一篇文章我们已经得到了mapper的代理对象,接下来我们对demoMapper.getDemo(1)这种语句进行分析.由于返回的mapper是个代理对象,因此会进入invoke方法,接下来我们来看 ...
- mybatis随笔五之Executor
在上一篇文章我们分析到了mapper接口方法的实现实际上是交由代理类来实现的,并最终调用Executor来查询,接下来我们对executor.query(ms, wrapCollection(para ...
- 关于Mybatis的一些随笔
Mapper.xml头文件 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http:/ ...
- MyBatis 简介
MyBatis的前身叫iBatis,本是apache的一个开源项目, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis ...
- 32、mybatis
第一章回顾jdbc开发 1)优点:简单易学,上手快,非常灵活构建SQL,效率高 2)缺点:代码繁琐,难以写出高质量的代码(例如:资源的释放,SQL注入安全性等) 开发者既要写业务逻辑,又要写对象的创建 ...
- 如何使用mybatis《三》
在前边阐述了单独使用mybatis的方法,在实际开发过程中mybatis经常和spring一起使用,即mybatis和spring进行集成,现在我们来看如何集成. mybatis和spring进行集成 ...
随机推荐
- Redis的appendfsync参数详解
redis.conf中的appendfysnc是对redis性能有重要影响的参数之一.可取三种值:always.everysec和no. 设置为always时,会极大消弱Redis的性能,因为这种模式 ...
- MyBatis(二)最简易的增、删、改、查
这篇是承接上一篇的helloWorld程序.首先将一系列初始化的步骤再做一个工厂类进行包装,代码如下: package com.tinaluo.sun; import java.io.InputStr ...
- 使用Kotlin&Anko, 扔掉XML开发Android应用
尝鲜使用Kotlin写了一段时间Android.说大幅度的减少了Java代码一点不夸张.用Java的时候动不动就new一个OnClickListener()匿名类,动不动就类型转换的地方都可以省下很多 ...
- Linux入门(9)——Ubuntu16.04安装flash player
打开网页经常提示安装flash player,不安装flash player很多网页的视频都看不了. Ubuntu16.04安装flash player 打开终端,输入: sudo apt-get i ...
- 判断页面是app打开还是浏览器打开。cookie
有个需求需要对页面判断不同的打开方式来最里面的链接进行不同调整, 这样就要分四种情况,app,浏览器X安卓系统,苹果系统,起初是对页面url地址带有的参数(安卓)跟用户代理(苹果)返回值判断navig ...
- noip第18课作业
1. 银行取款 [题目描述] 在现代文明社会中,大家在诸如银行办理业务.车站买票等活动时都很文明没有插队的现象,本着“先来先服务”的规矩. 新年马上到了,明明的爸爸打算上银行去取点钱,带着一向表现很好 ...
- QOpenglWidget 与QGLWidget的选择
1. QGLWidget 是Qt OpenGL模块,但是从其官方说明,推荐在Qt5.4 之后,使用QOpenglWidget版本,具体说明如下: Note: This class is part of ...
- Python自动化开发 - 模块与包
本节内容 一.模块 1.import module 2.from module import 3.from module import * 4.模块的__name__属性 5.模块搜索路径 6. ...
- Linux查看History记录加时间戳小技巧
Linux查看History记录加时间戳小技巧 熟悉bash的都一定知道使用history可以输出你曾经输入过的历史命令,例如[root@servyou_web ~]# history | more ...
- nuget.org无法解析的办法
今天想学习ef框架,就着手安装最新的ef啦.可是遇到了问题,提示 未能解析此远程名称:'nuget.org' 就去上网找资料啦,发现原来是被墙了,表示无奈. 网上的资料提示,修改hosts文件或是dn ...