映射文件的解析过程是配置文件解析过程的一部分,MyBatis 会在解析配置文件的过程中对映射文件进行解析。解析逻辑封装在 mapperElement 方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 // -☆- XMLConfigBuilder private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { // 获取 <package> 节点中的 name 属性 String mapperPackage = child.getStringAttribute("name"); // 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置 configuration.addMappers(mapperPackage); } else { // 获取 resource/url/class 等属性 String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // resource 不为空,且其他两者为空,则从指定路径中加载配置 if (resource != null && url == null && mapperClass == null){ ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder( inputStream, configuration, resource, configuration.getSqlFragments()); // 解析映射文件 mapperParser.parse(); // url 不为空,且其他两者为空,则通过 url 加载配置 } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder( inputStream, configuration, url, configuration.getSqlFragments()); // 解析映射文件 mapperParser.parse(); // mapperClass 不为空,且其他两者为空, // 则通过 mapperClass 解析映射配置 } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); // 以上条件不满足,则抛出异常 } else { throw new BuilderException("……"); } } } } }
主要逻辑是遍历mappers的子节点,并根据节点属性值判断通过何种方式加载映射文件或映射信息。这里把配置在注解中的内容称为映射信息,以XML为载体的配置称为映射文件。
在 MyBatis 中,共有四种加载映射文件或映射信息的方式:
从文件系统中加载映射文件
通过 URL 的方式加载映射文件
通过 mapper接口加载映射信息,映射信息可以配置在注解中,也可以配置在映射文件中
通过包扫描的方式获取到某个包下的所有类,并使用第三种方式为每个类解析映射信息
需要注意的是,在 MyBatis中,通过注解配置映射信息的方式是有一定局限性的,这一点 MyBatis 官方文档中描述的比较清楚。
因为最初设计时,MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射 语句也是定义在 XML 中的。而到了 MyBatis3,就有新选择了。MyBatis3 构建在全面且强 大的基于 Java 语言的配置 API 之上。这个配置 API 是基于 XML 的 MyBatis 配置的基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而不会引入大量的开销
注意:不幸的是,Java 注解的表达力和灵活性十分有限 。尽管很多时间都花在调查、设计和试验上,最强大的MyBatis映射并不能用注解来构建 ——并不是在开玩笑,的确是这样。
因此,对于一些较为复杂的配置信息,我们还是应该通过XML 的方式进行配置。
映射文件解析入口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // -☆- XMLMapperBuilder public void parse() { // 检测映射文件是否已经被解析过 if (!configuration.isResourceLoaded(resource)) { // 解析 mapper 节点 configurationElement(parser.evalNode("/mapper")); // 添加资源路径到“已解析资源集合”中 configuration.addLoadedResource(resource); // 通过命名空间绑定 Mapper 接口 bindMapperForNamespace(); } // 处理未完成解析的节点 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
映射文件解析入口逻辑包含三个核心操作,如下:
解析 mapper 节点
通过命名空间绑定 Mapper 接口
处理未完成解析的节点