Mybatis之Configuration初始化(配置文件.xml的解析)

源码解读第一步我觉着应该从Mybatis如何解析配置文件开始。

1.先不看跟Spring集成如何解析,先看从SqlSessionFactoryBuilder如果解析的。

1 String resouce = "conf.xml";
2 InputStream is = Resources.getResourceAsStream(resouce);
3
4 // 构建sqlSession工厂
5 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

SqlSessionFactoryBuilder

 1   public SqlSessionFactory build(InputStream inputStream) {
 2     return build(inputStream, null, null);
 3   }
 4
 5   public SqlSessionFactory build(InputStream inputStream, String environment) {
 6     return build(inputStream, environment, null);
 7   }
 8
 9   public SqlSessionFactory build(InputStream inputStream, Properties properties) {
10     return build(inputStream, null, properties);
11   }
12   //上面那么多同名不同参的方法最后都会进入这个方法   //支持传入Enviromment.properties实际上是支持动态传入覆盖全局配置xml的内容
13   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
14     try {
15       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      //因为从下面的Build方法可以看出 parser.parse()后Configuration就初始化好了
16       return build(parser.parse());
17     } catch (Exception e) {
18       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
19     } finally {
20       ErrorContext.instance().reset();
21       try {
22         inputStream.close();
23       } catch (IOException e) {
24         // Intentionally ignore. Prefer previous error.
25       }
26     }
27   }
   public SqlSessionFactory build(Configuration config) {      return new DefaultSqlSessionFactory(config);   }

真正初始化Configuration的类是XMLConfigBuilder

 1   public Configuration parse() {    //这一块可以看出来 全局Configuration只会初始化一次,实例化时候是false
 2     if (parsed) {
 3       throw new BuilderException("Each XMLConfigBuilder can only be used once.");
 4     }
 5     parsed = true;
 6     parseConfiguration(parser.evalNode("/configuration"));
 7     return configuration;
 8   }
 9   //获取配置文件整个/configuration节点内容
10   private void parseConfiguration(XNode root) {
11     try {
12       Properties settings = settingsAsPropertiess(root.evalNode("settings")); //初始化配置文件中全局变量
13       //issue #117 read properties first
14       propertiesElement(root.evalNode("properties"));//初始化xml中的配置文件,同时讲其中的变量保存起来,因为可能其他地方引用
15       loadCustomVfs(settings); //加载自定义的VFS实现类? 这个什么用?
16       typeAliasesElement(root.evalNode("typeAliases")); //加载typeAliases别名初始化
17       pluginElement(root.evalNode("plugins")); //加载插件,实际上就是拦截器
18       objectFactoryElement(root.evalNode("objectFactory"));// //加载自定义的对象工厂
19       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //加载自定义的处理驼峰方式的key的处理器
20       reflectionFactoryElement(root.evalNode("reflectionFactory")); //加载自定义的反射器  没用过?
21       settingsElement(settings); //将setting的属性,设置到Configuration对象属性中
22       // read it after objectFactory and objectWrapperFactory issue #631
23       environmentsElement(root.evalNode("environments"));
24       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
25       typeHandlerElement(root.evalNode("typeHandlers")); //初始化类型处理器
26       mapperElement(root.evalNode("mappers")); //处理mappers节点内容,实际上就是初始化MaperStatement
27     } catch (Exception e) {
28       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
29     }
30   }

因为大部分方法都比较简单,我这里只介绍几个我认为比较重要的。

① typeAliasesElement(root.evalNode("typeAliases")); //加载typeAliases别名初始化

 1   private void typeAliasesElement(XNode parent) {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { //配置的是包名 扫描包里所有的类。放入的key默认是注解的key
 5           String typeAliasPackage = child.getStringAttribute("name");
 6           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
 7         } else {
 8           String alias = child.getStringAttribute("alias");
 9           String type = child.getStringAttribute("type");
10           try {
11             Class<?> clazz = Resources.classForName(type);
12             if (alias == null) {
13               typeAliasRegistry.registerAlias(clazz);
14             } else {
15               typeAliasRegistry.registerAlias(alias, clazz);
16             }         //实际上就是存在了TypeAliasRegistry类的一个私有map里面。
17           } catch (ClassNotFoundException e) {
18             throw new BuilderException("Error registering typeAlias for ‘" + alias + "‘. Cause: " + e, e);
19           }
20         }
21       }
22     }
23   }
1 public class TypeAliasRegistry {
2
3   private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();  。。。。。。
4 }

② pluginElement(root.evalNode("plugins")); 加载插件,便于后期理解拦截器原理

 1   private void pluginElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         String interceptor = child.getStringAttribute("interceptor");
 5         Properties properties = child.getChildrenAsProperties();       //获取interceptor 这里resolveClass实际上就是到TypeAliasResitstry里面找一下,找到了获取class,没有直接用class去反射回去对象
 6         Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
 7         interceptorInstance.setProperties(properties);       //获取对象后调用configuration方法。
 8         configuration.addInterceptor(interceptorInstance);
 9       }
10     }
11   }

Configuration

1  public void addInterceptor(Interceptor interceptor) {
2     interceptorChain.addInterceptor(interceptor);
3   }

InterceptorChain

1   public void addInterceptor(Interceptor interceptor) {
2     interceptors.add(interceptor);
3   }

这一块就是放Configuration的拦截器链里面添加拦截器。这一块现在知道是在这时候添加的就好了。后面介绍Mybatis的拦截器的时候深入了解。

③ mapperElement(root.evalNode("mappers")); //处理mappers节点内容,实际上就是初始化MaperStatement

 1   private void mapperElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) {
 5           String mapperPackage = child.getStringAttribute("name");
 6           configuration.addMappers(mapperPackage);
 7         } else {
 8           String resource = child.getStringAttribute("resource");
 9           String url = child.getStringAttribute("url");
10           String mapperClass = child.getStringAttribute("class");        //xml文件中mapper节点,配置resource,url是执行的mapper.xml文件的位置,所以现在只分析这一块。
11           if (resource != null && url == null && mapperClass == null) {
12             ErrorContext.instance().resource(resource);
13             InputStream inputStream = Resources.getResourceAsStream(resource);
14             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
15             mapperParser.parse();
16           } else if (resource == null && url != null && mapperClass == null) {
17             ErrorContext.instance().resource(url);
18             InputStream inputStream = Resources.getUrlAsStream(url);
19             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
20             mapperParser.parse();
21           } else if (resource == null && url == null && mapperClass != null) {
22             Class<?> mapperInterface = Resources.classForName(mapperClass);
23             configuration.addMapper(mapperInterface);
24           } else {
25             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
26           }
27         }
28       }
29     }
30   }

可以看出来resource和url都是通过XMLMapperBuilder来解析的。下面来看下parse方法。

 1   private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
 2     super(configuration);       //前面还有个构造器,是根据传入的inputstream构造XPathParser,parser可以拿到resouce里面的内容
 3     this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
 4     this.parser = parser;
 5     this.sqlFragments = sqlFragments;
 6     this.resource = resource;
 7   }
 8
 9   public void parse() {
10     if (!configuration.isResourceLoaded(resource)) { 是否加载过改文件,
11       configurationElement(parser.evalNode("/mapper")); 来解析mapper文件内容
12       configuration.addLoadedResource(resource); 添加加载记录
13       bindMapperForNamespace();往configration添加命名空间的代理对象
14     }
15
16     parsePendingResultMaps();
17     parsePendingChacheRefs();
18     parsePendingStatements();
19   }
 1   private void configurationElement(XNode context) { //解析mapper.xml子节点的内容
 2     try {
 3       String namespace = context.getStringAttribute("namespace");
 4       if (namespace == null || namespace.equals("")) {
 5         throw new BuilderException("Mapper‘s namespace cannot be empty");
 6       }
 7       builderAssistant.setCurrentNamespace(namespace);
 8       cacheRefElement(context.evalNode("cache-ref"));
 9       cacheElement(context.evalNode("cache"));
10       parameterMapElement(context.evalNodes("/mapper/parameterMap"));
11       resultMapElements(context.evalNodes("/mapper/resultMap")); //解析resultMap 很复杂,后面单独解读
12       sqlElement(context.evalNodes("/mapper/sql"));
13       buildStatementFromContext(context.evalNodes("select|insert|update|delete")); //初始化这几个类型节点的内容
14     } catch (Exception e) {
15       throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
16     }
17   }
 1   private void buildStatementFromContext(List<XNode> list) {       因为会有多个节点,所以是一个List<XNode>
 2     if (configuration.getDatabaseId() != null) {
 3       buildStatementFromContext(list, configuration.getDatabaseId());
 4     }
 5     buildStatementFromContext(list, null);
 6   }
 7
 8   private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
 9     for (XNode context : list) {
10       final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
11       try {
12         statementParser.parseStatementNode();
13       } catch (IncompleteElementException e) {
14         configuration.addIncompleteStatement(statementParser);
15       }
16     }
17   }

从上面可以看出来最终是由XMLStatementBuilder来解析我们的写的sql部分。

 1   public void parseStatementNode() {
 2     String id = context.getStringAttribute("id");
 3     String databaseId = context.getStringAttribute("databaseId");
 4
 5     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
 6       return;
 7     }
 8
 9     Integer fetchSize = context.getIntAttribute("fetchSize");
10     Integer timeout = context.getIntAttribute("timeout");
11     String parameterMap = context.getStringAttribute("parameterMap");
12     String parameterType = context.getStringAttribute("parameterType");
13     Class<?> parameterTypeClass = resolveClass(parameterType);
14     String resultMap = context.getStringAttribute("resultMap");
15     String resultType = context.getStringAttribute("resultType");
16     String lang = context.getStringAttribute("lang");
17     LanguageDriver langDriver = getLanguageDriver(lang);
18
19     Class<?> resultTypeClass = resolveClass(resultType);
20     String resultSetType = context.getStringAttribute("resultSetType");
21     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
22     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
23
24     String nodeName = context.getNode().getNodeName();
25     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
26     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
27     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
28     boolean useCache = context.getBooleanAttribute("useCache", isSelect);
29     boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
30
31     // Include Fragments before parsing
32     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
33     includeParser.applyIncludes(context.getNode());
34
35     // Parse selectKey after includes and remove them.
36     processSelectKeyNodes(id, parameterTypeClass, langDriver);
37
38     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
39     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
40     String resultSets = context.getStringAttribute("resultSets");
41     String keyProperty = context.getStringAttribute("keyProperty");
42     String keyColumn = context.getStringAttribute("keyColumn");
43     KeyGenerator keyGenerator;
44     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
45     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
46     if (configuration.hasKeyGenerator(keyStatementId)) {
47       keyGenerator = configuration.getKeyGenerator(keyStatementId);
48     } else {
49       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
50           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
51           ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
52     }
53   获取节点所有属性内容,调用builderAssistant,实现是MapperBuilderAssistant 在XmlMapperBuilder构造器初始化时候就制定了
54     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
55         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
56         resultSetTypeEnum, flushCache, useCache, resultOrdered,
57         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
58   }
 1   public MappedStatement addMappedStatement(
 2       String id,
 3       SqlSource sqlSource,
 4       StatementType statementType,
 5       SqlCommandType sqlCommandType,
 6       Integer fetchSize,
 7       Integer timeout,
 8       String parameterMap,
 9       Class<?> parameterType,
10       String resultMap,
11       Class<?> resultType,
12       ResultSetType resultSetType,
13       boolean flushCache,
14       boolean useCache,
15       boolean resultOrdered,
16       KeyGenerator keyGenerator,
17       String keyProperty,
18       String keyColumn,
19       String databaseId,
20       LanguageDriver lang,
21       String resultSets) {
22
23     if (unresolvedCacheRef) {
24       throw new IncompleteElementException("Cache-ref not yet resolved");
25     }
26
27     id = applyCurrentNamespace(id, false); //把id加上namespace+"."
28     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
29
30     MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
31         .resource(resource)
32         .fetchSize(fetchSize)
33         .timeout(timeout)
34         .statementType(statementType)
35         .keyGenerator(keyGenerator)
36         .keyProperty(keyProperty)
37         .keyColumn(keyColumn)
38         .databaseId(databaseId)
39         .lang(lang)
40         .resultOrdered(resultOrdered)
41         .resulSets(resultSets)
42         .resultMaps(getStatementResultMaps(resultMap, resultType, id))
43         .resultSetType(resultSetType)
44         .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
45         .useCache(valueOrDefault(useCache, isSelect))
46         .cache(currentCache);
47
48     ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
49     if (statementParameterMap != null) {
50       statementBuilder.parameterMap(statementParameterMap);
51     }
52
53     MappedStatement statement = statementBuilder.build();
54     configuration.addMappedStatement(statement);
55     return statement;
56   }
MappedStatement.Builder 是MappedStatement的内部类。里面有MappedStatement的引用,所有方法都设置内部引用mappedStatement的属性,并返回自身,所以这一块就是类似于setparam()最终通过build方法 返回对象。 然后调用Congifuration.addMappedStatement保存到Congifuration对象里面。
1   public void addMappedStatement(MappedStatement ms) {
2     mappedStatements.put(ms.getId(), ms);
3   }

到此Congifuration对象初始话完事了...  全局只有一个。

原文地址:https://www.cnblogs.com/haoerlv/p/9930100.html

时间: 2024-08-03 01:20:22

Mybatis之Configuration初始化(配置文件.xml的解析)的相关文章

MyBatis原理分析之三:初始化(配置文件读取和解析)

1. 准备工作 编写测试代码(具体请参考<Mybatis入门示例>),设置断点,以Debug模式运行,具体代码如下: Java代码   String resource = "mybatis.cfg.xml"; Reader reader = Resources.getResourceAsReader(resource); SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader); SqlSess

MyBatis 源码分析 - 配置文件解析过程

* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAliases和typeHandlers等,本文的篇幅也主要在对这三个配置解析过程的分析上.下面,我们来一起看一下本篇文章的目录结构. 从目录上可以看出,2.3节.2.5节和2.8节的内容比较多.其中2.3节是关于settings配置解析过程的分析,除了对常规的 XML 解析过程分析,本节额外的分析了元

Mybatis深入之初始化过程

Mybatis深入之初始化过程 一:简介 这篇开始是根据Mybatis源码来对Mybatis进行更深入的学习.当然.精力有限.还做不到学习的面面俱到. Mybatis初始化过程可以用一句话概括:就是将Mybatis的配置信息加载到一个类中.供后面Mybatis进行各种操作时使用.这个类叫:Configuration--见名知意.当然这个类的功能并不仅限与存放配置文件信息. 二:整体流程 下面是一段正常情况下从加载配置到执行sql语句的代码: String mybatisConfigPath =

Maven 项目使用mybatis的环境搭建-基于xml形式实现查询所有的功能

首先了解一下什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型.接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录. 本例子将通过一个简单的查询所有的用户信息的动作来简单的解析如何在Maven项目中使用mybatis. 1.本

Mybatis(1、核心配置文件、Properties、Settings、typeAliases...)

Mybatis(1.核心配置文件.Properties.Settings.typeAliases...) 2017年04月23日 22:52:36 阅读数:1527 此章主要介绍sqlMapConfiguragion.xml的核心配置属性. mybatis.mappers.Environments.ObjectFactory.typeHandlers.Properties.Settings.typeAliases.Plugins 原链接为:https://blog.csdn.net/www105

log4j2配置文件xml详细了解

log4j2配置文件xml详细了解 详细参考:https://www.cnblogs.com/new-life/p/9246143.html log4j 2.x版本不再支持像1.x中的.properties后缀的文件配置方式,2.x版本配置文件后缀名只能为".xml",".json"或者".jsn". 系统选择配置文件的优先级(从先到后)如下: (1).classpath下的名为log4j2-test.json 或者log4j2-test.jsn

PHP XML Expat 解析器

PHP XML Expat 解析器 内建的 Expat 解析器使在 PHP 中处理 XML 文档成为可能. XML 是什么? XML 用于描述数据,其焦点是数据是什么.XML 文件描述了数据的结构. 在 XML 中,没有预定义的标签.您必须定义自己的标签. 如需学习更多关于 XML 的知识,请访问我们的 XML 教程. Expat 是什么? 如需读取和更新 - 创建和处理 - 一个 XML 文档,您需要 XML 解析器. 有两种基本的 XML 解析器类型: 基于树的解析器:这种解析器把 XML

Android中XML数据解析

转载请注明出处:http://blog.csdn.net/yegongheng/article/details/38296207 XML初步 今天我们来学习另一种非常重要的数据交换格式-XML.XML(Extensible Markup Language的缩写,意为可扩展的标记语言),它是一种元标记语言,即定义了用于定义其他特定领域有关语义的.结构化的标记语言,这些标记语言将文档分成许多部件并对这些部件加以标识.XML 文档定义方式有:文档类型定义(DTD)和XML Schema.DTD定义了文

iOS网络编程解析协议二:XML数据传输解析

XML两种解析方式,一种是SAX,NSXMLParser是SAX方法解析,另一种是DOM(Document Object Model); 区别: SAX: 只能读,不能修改,只能顺序访问,适合解析大型XML,解析速度快 常应用于处理大量数据的XML,实现异构系统的数据访问,实现跨平台 从文档的开始通过每一节点移动,定位一个特定的节点 DOM: 不仅能读,还能修改,而且能够实现随机访问,缺点是解析速度慢,适合解析小型文档 一般应用与小型的配置XML,方便操作 为载入到内存的文档节点建立类型描述,呈