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进行集成 ...
随机推荐
- ubuntu server sudo出现sudo:must be setuid root 完美解决办法
原文链接:http://blog.csdn.net/supercrsky/article/details/9788397 1.开机按shift或esc先进行recovery模式 2.选择root命令行 ...
- HP 集群软件 - 不能接收节点的设备查询信息:软件引起的连接失败
问题 # cmcheckconf -v -C /etc/cmcluster/cmclconfig.ascii Begin cluster verification... Checking clust ...
- Scala界面事件处理编程实战详解.
今天学习了一个Scala界面事件处理编程,让我们从代码出发. import scala.swing._import scala.swing.event._ object GUI_Panel exten ...
- 关于QT建立项目中遇到的相关问题的处理办法
关于QT建立项目中遇到的相关问题的处理办法 1.在QTCreater中新建一个空的工程时,第一次运行会抱一个错误,如下: -1: error: LNK1123: 转换到 COFF 期间失败: 文件无效 ...
- 通过mybatis向数据库中插入日期数据
遇到的问题: 通过mybatis向数据库中插入日期格式数据,发现只有年月日, 没有小时分钟和秒 当你想在实体类中使用java.util.Date类型,而且还想在数据库中保存时分秒时, 解决办法: 你可 ...
- input和raw_input
Python2.X使用raw_input() Python3.X废弃了raw_input()函数,使用input()函数替代它 code: data=input("please input ...
- 不合法的DB Index
redis报错,Invalid Db Index . 需要清理一下redis, 进入redis文件加下,登录redis, redis-cli ,输入账号密码, flush all, 回收程序池,重新生 ...
- C#导入Excel数据常见问题
今天在做一个excle数据导入的时候遇到了一个奇葩问题,项目使用的是MVC,在VS2010里面调试的时候没有问题,可是当发布到本地IIS或服务器上时就出现了问题: 1.excel文件正在被使用: 2. ...
- Could not load file or assembly 'Microsoft.AnalysisServices.SharePoint.Integration'
In Central Administration, in System Settings, click Manage farm solutions. Click Powerpivotwebapp. ...
- 背水一战 Windows 10 (40) - 控件(导航类): AppBar, CommandBar
[源码下载] 背水一战 Windows 10 (40) - 控件(导航类): AppBar, CommandBar 作者:webabcd 介绍背水一战 Windows 10 之 控件(导航类) App ...