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. unittest框架下的HTMLTestRunner报告模块使用及优化

    引言 在做接口自动化测试的时候,使用python单元测试框架unittest下HTMLTestRunner报告模板,可以很好的展示我们测试结果的数据. 官方的标准版模板地址:http://tungwa ...

  2. 为什么 K8s 在阿里能成功(转)

    为什么 K8s 在阿里能成功?| 问底中国 IT 技术演进   作者:曾凡松 阿里云云原生应用平台高级技术专家张振 阿里云云原生应用平台高级技术专家 导读:本文描述了阿里巴巴在容器管理领域的技术演进历 ...

  3. 查询linux版本信息

    uname -a (Linux查看版本当前操作系统内核信息) cat /proc/version (Linux查看当前操作系统版本信息) cat /etc/redhat-release (Linux查 ...

  4. 无缘诺贝尔奖的George Dantzig——线性规划之父

    无缘诺贝尔奖的George Dantzig——线性规划之父 王军强,2012年11月2日 “线性规划之父”的George Dantzig,与“计算机之父”.“博弈论之父”John Von Neuman ...

  5. linq和匿名方法、委托、匿名委托、lambda

    委托相当于JavaScript中的闭包,c++中的函数指针. c#为了引进这个函数指针,将其进行包装成“委托”,同时将非托管的变成托管的. 1.最初的委托该怎么用 弊端:写的代码量过多,还要写一个显示 ...

  6. COMMUNITY DETECTION_python-louvain

    Python-louvain Package pip install python-louvain import community #first compute the best partition ...

  7. Python爬虫之post请求

    暑假放假在家没什么事情做,所以在学习了爬虫,在这个博客园里整理记录一些学习的笔记. 构建表单数据(以http://www.iqianyue.com/mypost 这个简单的网页为例) 查看源代码,发现 ...

  8. Linux find命令:在目录中查找文件(超详解)

    find 是 Linux 中强大的搜索命令,不仅可以按照文件名搜索文件,还可以按照权限.大小.时间.inode 号等来搜索文件.但是 find 命令是直接在硬盘中进行搜索的,如果指定的搜索范围过大,f ...

  9. 用apscheduler写python定时脚本

    apscheduler 官方文档:http://apscheduler.readthedocs.io/en/latest/ 写一个后台定时任务,一般2个选择,一个是apscheduler,一个cele ...

  10. 智能指针和异常、 weak_ptr、unique_ptr

    12.1.4智能指针和异常 1.在块中创建的动态内存,如果是由内置指针来指向这块内存,那么若是在块结束时未delete这个指针,则该内存不会被释放,若在delete之前发生异常,由于还没执行delet ...