buildSqlSessionFactory() 这个方法比较长, 干的事情也比较多. 包括一些别名, 插件, 类型处理器等的解析. 
从主流程上来看, 最主要的其实是干了两件事:
1. 对 mapper.xml 文件进行解析
2. 使用 SqlSessionFactoryBuilder 创建 sqlSessionFactory mapper.xml的扫描工作不在这个方法里, 但是放到这里来看, 会更加清晰一点.

1. mapperLocation 的解析

  @Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
......if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
} return factory.getObject();
}

在 SqlSessionFactory 的创建方法中, 执行了一个  this.properties.resolveMapperLocations() 方法.

  public Resource[] resolveMapperLocations() {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (this.mapperLocations != null) {
for (String mapperLocation : this.mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException e) {
// ignore
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
PathMatchingResourcePatternResolver 是org.springframework.core 里面的一个类, 是 spring 提供的. 
可以对 mapperLocations = classpath:mapper/**Mapper.xml 进行解析, 并拿到匹配路径下的文件资源

此例中得到的结果是 SchoolMapper.xml 和 UserMapper.xml 文件的 Resource . 并将它们放入了 mapperLocations 属性中.

2. mapper.xml 文件的解析

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
......
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
} try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
} if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
} return this.sqlSessionFactoryBuilder.build(configuration);
}

这里的  this.mapperLocations 就是 SchoolMapper.xml 和 UserMapper.xml 的 Resource.

这里主要关注一下 parse() 方法.

  public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}

2.1  解析mapper.xml配置文件

  private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}

这个方法中, 对xml中的配置进行解析, 并使用  SqlSource  进行记录. 最终封装进 Configuration 的 MappedStatement  mappedStatement 属性中

如例子中的 getById方法:

这里注意一个方法:

  public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}

这里的id是怎么来的呢?

MapperBuilderAssistant.java
  public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) {
return null;
}
if (isReference) {
// is it qualified with any namespace yet?
if (base.contains(".")) {
return base;
}
} else {
// is it qualified with this namespace yet?
if (base.startsWith(currentNamespace + ".")) {
return base;
}
if (base.contains(".")) {
throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
}
}
return currentNamespace + "." + base;
}

一番解析之后, UserMapper.xml 的getById 的 id = "com.study.demo.mybatis.mapper.SchoolMapper.getById"

2.2 绑定

  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 进行绑定.

在addMapper 方法中, 执行了一个重要方法:  getSqlSourceFromAnnotations()

这个方法其实就是对 接口方法进行注解检测的.

此例中, 如果将 SchoolMapper.xml 中的配置删掉, 然后改写SchoolMapper.java 中的getById方法

    @Select("select id, name from school where id = #{id}")
public School getById(@Param("id") Integer id);

那么就会在这里解析并赋值.

那如果我即配置了 mapper.xml 文件, 又配置了 @Select 注解的情况下, 到底是 mapper.xml 起作用还是 @Select 起作用呢?

1. 当然, 首先不可能都起作用, 这个是很明确的, 不然出现歧义的时候, 是咋个处理呢?

2. 在解析@Select 之前, 其实已经解析过 mapper.xml 了, 如果xml已经解析并且记录, 那么即使 @Select 解析失败, 那是不是起码还有一个可以使用呢?

事实上, 不能那样使用, 会报错的. 且会导致 mapper.xml 也用不上了. 程序启动也会中断, 不能继续进行. 

Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.study.demo.mybatis.mapper.SchoolMapper.getById

3. SqlSessionFactory创建

  //  SqlSessionFactoryBuilder.java
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

这里创建了一个默认的实现类 :  DefaultSqlSessionFactory, 且持有了 Configuration 的引用.

到这里, mapper.java(还未创建实例) , mapper.xml 都已经找到了.

mybatis - buildSqlSessionFactory()的更多相关文章

  1. [DEBUG]-[org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:431)] 一直在创建sqlsession工厂原因

    今天在做开发项目的时候出现一直在控台输出创建sqlsessionfactory'这个代码, tomcat一直在控制台上输出这个内容无法停止下来,那么到底是什么原因呢, 我们可以在输出信息上看到有个wa ...

  2. mybatis笔记3 一些原理的理解

    1,mybatis流程跟踪,原理理解 基本思路: 从SqlSessionFactory的初始化出发,观察资源的准备和环境的准备,以及实现持久层的一些过程: 进入SqlSessionFactoryBea ...

  3. MyBatis 配置文件头部换行异常

    INFO - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory ...

  4. Exception mybatis 配置文件:<typeAlias alias="***" type="***"/> 重复配置

    INFO - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory ...

  5. 使用XSD校验Mybatis的SqlMapper配置文件(1)

    这篇文章以前面对SqlSessionFactoryBean的重构为基础,先简单回顾一下做了哪些操作: 新建SqlSessionFactoryBean,初始代码和mybatis-spring相同: 重构 ...

  6. 重构Mybatis与Spring集成的SqlSessionFactoryBean(2)

    三.代码重构 1.先使用Eclipse把buildSqlSessionFactory()方法中众多的if换成小函数 protected SqlSessionFactory buildSqlSessio ...

  7. 重构Mybatis与Spring集成的SqlSessionFactoryBean(1)

    一般来说,修改框架的源代码是极其有风险的,除非万不得已,否则不要去修改.但是今天却小心翼翼的重构了Mybatis官方提供的与Spring集成的SqlSessionFactoryBean类,一来是抱着试 ...

  8. Mybatis解析动态sql原理分析

    前言 废话不多说,直接进入文章. 我们在使用mybatis的时候,会在xml中编写sql语句. 比如这段动态sql代码: <update id="update" parame ...

  9. mybatis Result Maps collection already contains value for com.ebways.dictionary.dao.impl.PtInfoDaoImpl.beanMap

    java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.conte ...

随机推荐

  1. ANDROID开发之问题积累及解决方案(四)

    首先贴出问题类型: 程序无法启动,查看logcat,提示如下信息: W/dalvikvm(679): PR_CAPBSET_DROP 32 failed: Invalid argument. Plea ...

  2. Java连载84-Collection的常用方法、迭代器

    一.Collections的常用方法介绍 1.承接上次连载,先介绍几个简单的常用方法 package com.bjpowernode.java_learning; import java.util.* ...

  3. npm 配置国内源

    淘宝镜像 npm config set registry http://registry.npm.taobao.org

  4. php header的使用

    // okheader('HTTP/1.1 200 OK'); //设置一个404头:header('HTTP/1.1 404 Not Found'); //设置地址被永久的重定向header('HT ...

  5. SQL Server 检查和处理死锁问题

    SELECT spid, blocked, DB_NAME(sp.dbid) AS DBName, program_name, waitresource, lastwaittype, sp.login ...

  6. NMF: non-negative matrix factorization.

    1. 矩阵分解可以用来解决什么方法, 以及how? 利用矩阵分解来解决实际问题的分析方法很多,如PCA(主成分分析).ICA(独立成分分析).SVD(奇异值分解).VQ(矢量量化)等.在所有这些方法中 ...

  7. 记manjaro图形驱动删除后的一次补救

    #一.前言 众所周知,NVIDIA的闭源驱动在Linux上的兼容性不是很好,再加上我不玩游戏,于是我就想卸载独显只留核显.我以为我装了独显和核显两种驱动,原本想直接删除独显驱动,没想到删除的是bumb ...

  8. AcWing 791. 高精度加法

    https://www.acwing.com/problem/content/793/ #include<bits/stdc++.h> using namespace std; vecto ...

  9. jenkins常用

    记最精简版的启动:gradle集成springboot+vue 安装jenkins,安装待推荐插件 服务器安装gradle 服务器安装npm 配置git仓库,配置git开发者账号等 配置gradle构 ...

  10. 来了,就给自己立个flag

    2019-09-16,刚刚申请的博客园. 不知道不觉,自己已经大四毕业了. 说来惭愧,已经接触IT这方面已经四年了. 但仍然感觉自己像个萌新,啥也不懂,这也不会,那也不会. 8月开始在公司大佬的指导下 ...