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的更多相关文章

  1. mybatis随笔三之SqlSession

    在上一篇文章我们已经得到了DefaultSqlSession,接下来我们对sqlSession.getMapper(DemoMapper.class)这种语句进行分析 @Override public ...

  2. mybatis随笔二之SqlSessionFactory

    在上一篇文章我们已经得到了DefaultSqlSessionFactory @Override public SqlSession openSession() { return openSession ...

  3. MyBatis随笔

    前一阵参与了一个项目的搭建,为了快速开发再加上学一些新东西,准备采用React+Spring MVC+MyBatis的架构. 花了一些时间最终把Spring MVC+MyBatis打通. 这里总结下M ...

  4. mybatis随笔四之MapperProxy

    在上一篇文章我们已经得到了mapper的代理对象,接下来我们对demoMapper.getDemo(1)这种语句进行分析.由于返回的mapper是个代理对象,因此会进入invoke方法,接下来我们来看 ...

  5. mybatis随笔五之Executor

    在上一篇文章我们分析到了mapper接口方法的实现实际上是交由代理类来实现的,并最终调用Executor来查询,接下来我们对executor.query(ms, wrapCollection(para ...

  6. 关于Mybatis的一些随笔

    Mapper.xml头文件 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http:/ ...

  7. MyBatis 简介

    MyBatis的前身叫iBatis,本是apache的一个开源项目, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis ...

  8. 32、mybatis

    第一章回顾jdbc开发 1)优点:简单易学,上手快,非常灵活构建SQL,效率高 2)缺点:代码繁琐,难以写出高质量的代码(例如:资源的释放,SQL注入安全性等) 开发者既要写业务逻辑,又要写对象的创建 ...

  9. 如何使用mybatis《三》

    在前边阐述了单独使用mybatis的方法,在实际开发过程中mybatis经常和spring一起使用,即mybatis和spring进行集成,现在我们来看如何集成. mybatis和spring进行集成 ...

随机推荐

  1. mybatis spring boot深入

    [size=large]头一篇博文说了整合了spring boot和mybatis大家都可以对后台数据进行操作但是如何才能进行高效的操作数据库节省代码,下面来一起谈论哈mybatis的通用mapper ...

  2. 阿里云oss如何上传一个文件夹

    最近公司在做工程项目,实现文件夹云存储上传 网上找了很久,发现很多项目都存在一些问题,但还是让我找到了一个成熟的项目. 工程: 对项目的文件夹云存储上传功能做出分析,找出文件夹上传的原理,对文件夹的云 ...

  3. C#控件之:进度条(ProgressBar)

    一.重绘进度条 public class CustomProgressBar:ProgressBar { public CustomProgressBar() { this.SetStyle(Cont ...

  4. js 构建map 和list

    //构建map function Map() { this.arr = new Array(); var struct = function(key, value) { this.key = key; ...

  5. kepware http接口 Objective-C开发

    读取某变量的值(NSURL #import <Foundation/Foundation.h> NSDictionary *headers = @{ @"Connection&q ...

  6. hadoop和spark相关参数的配置

    背景 MapReduce和Spark对外提供了上百个配置参数,用户可以为作业定制这些参数以更快,更稳定的运行应用程序.本文梳理了最常用的一些MapReduce和Spark配置参数. MapReduce ...

  7. NoSQL世界的几个重要理论

    和所有事物一样,NoSQL的兴起也是由许多理论支撑作为前提的,正是由下面一些理论的支撑,NoSQL的方向才能如此明朗. 1.CAP理论 CAP理论无疑是导致技术趋势由关系数据库系统向NoSQL系统转变 ...

  8. allegro中如何添加安装孔(注:在PCB图纸中添加)

    最近再给外国一家公司做某一个小的系统模块的封装,其中这个模块中间是挖空的,这就比较难办,到现在为止我还没有找到如何在封装中添加自己绘制特定形状的过孔,(倒是可以添加软件自带的一些圆形的安装孔)最终解决 ...

  9. POJ 2570 线段树

    Potted Flower Time Limit: 2000 MS Memory Limit: 65536 KB 64-bit integer IO format: %I64d , %I64u Jav ...

  10. 58VIP账号发贴器

    因公司有招聘大量普工需求,需要大量简历资源,直接从58买一份简历动辄几块到几十块,如果做精准少则1块以上的点击.而且收到的简历不太精准,应公司需求写了一款自动发贴器.完全模拟人工发贴,经过一个月的测试 ...