Java最新源码分析Mybatis MappedStatement的创建流程(1),阿里P8面试官都说太详细了

最后

俗话说,好学者临池学书,不过网络时代,对于大多数的我们来说,我倒是觉得学习意识的觉醒很重要,这是开始学习的转折点,比如看到对自己方向发展有用的信息,先收藏一波是一波,比如如果你觉得我这篇文章ok,先点赞收藏一波。这样,等真的沉下心来学习,不至于被找资料分散了心神。慢慢来,先从点赞收藏做起,加油吧!

另外,给大家安排了一波学习面试资料:

image

image

以上就是本文的全部内容,希望对大家的面试有所帮助,祝大家早日升职加薪迎娶白富美走上人生巅峰!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

1、上节回顾


我们注意到这里有两三个与Mapper相关的配置:

  1. SqlSessionFactory#mapperLocations,指定xml文件的配置路径。

  2. SqlSessionFactory#configLocation,指定mybaits的配置文件,该配置文件也可以配置mapper.xml的配置路径信息。

  3. MapperScannerConfigurer,扫描Mapper的java类(DAO)。

我们已经详细介绍了Mybatis Mapper对象的扫描与构建,那接下来我们将重点介绍MaperProxy与mapper.xml文件是如何建立关联关系的。

根据上面的罗列以及上文的讲述,Mapper.xml与Mapper建立联系主要的入口有三:

1)MapperScannerConfigurer扫描Bean流程中,在调用MapperReigistry#addMapper时如果Mapper对应的映射文件(Mapper.xml)未加载到内存,会触发加载。

2)实例化SqlSessionFactory时,如果配置了mapperLocations。

3)示例化SqlSessionFactory时,如果配置了configLocation。

本节的行文思路:从SqlSessionFacotry的初始化开始讲起,因为mapperLocations、configLocation都是是SqlSessionFactory的属性。

温馨提示:下面开始从源码的角度对其进行介绍,大家可以先跳到文末看看其调用序列图。

2、SqlSessionFacotry


if (xmlConfigBuilder != null) { // XMLConfigBuilder // @1

try {

xmlConfigBuilder.parse();

if (logger.isDebugEnabled()) {

logger.debug(“Parsed configuration file: '” + this.configLocation + “'”);

}

} catch (Exception ex) {

throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);

} finally {

ErrorContext.instance().reset();

}

}

if (!isEmpty(this.mapperLocations)) { // @2

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”);

}

}

上文有两个入口:

代码@1:处理configLocation属性。

代码@2:处理mapperLocations属性。

我们先从XMLConfigBuilder#parse开始进行追踪。该方法主要是解析configLocation指定的配置路径,对其进行解析,具体调用parseConfiguration方法。

2.1 XMLConfigBuilder

我们直接查看其parseConfiguration方法。

private void parseConfiguration(XNode root) {

try {

propertiesElement(root.evalNode(“properties”)); //issue #117 read properties first

typeAliasesElement(root.evalNode(“typeAliases”));

pluginElement(root.evalNode(“plugins”));

objectFactoryElement(root.evalNode(“objectFactory”));

objectWrapperFactoryElement(root.evalNode(“objectWrapperFactory”));

settingsElement(root.evalNode(“settings”));

environmentsElement(root.evalNode(“environments”)); // read it after objectFactory and objectWrapperFactory issue #631

databaseIdProviderElement(root.evalNode(“databaseIdProvider”));

typeHandlerElement(root.evalNode(“typeHandlers”));

mapperElement(root.evalNode(“mappers”)); // @1

} catch (Exception e) {

throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);

}

}

重点关注mapperElement,从名称与参数即可以看出,该方法主要是处理中mappers的定义,即mapper sql语句的解析与处理。如果使用过Mapper的人应该不难知道,我们使用mapper节点,通过resource标签定义具体xml文件的位置。

2.1.1XMLConfigBuilder#mapperElement

private void mapperElement(XNode parent) throws Exception {

if (parent != null) {

for (XNode child : parent.getChildren()) {

if (“package”.equals(child.getName())) {

String mapperPackage = child.getStringAttribute(“name”);

configuration.addMappers(mapperPackage);

} else {

String resource = child.getStringAttribute(“resource”);

String url = child.getStringAttribute(“url”);

String mapperClass = child.getStringAttribute(“class”);

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()); // @1

mapperParser.parse();

} 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();

} else if (resource == null && url == null && mapperClass != null) {

Class<?> mapperInterface = Resources.classForName(mapperClass);

configuration.addMapper(mapperInterface);

} else {

throw new BuilderException(“A mapper element may only specify a url, resource or class, but not more than one.”);

}

}

}

}

}

上面的代码比较简单,不难看出,解析出Mapper标签,解析出resource标签的属性,创建对应的文件流,通过构建XMLMapperBuilder来解析对应的mapper.xml文件。此时大家会惊讶的发现,在SqlSessionFacotry的初始化代码中,处理mapperLocations时就是通过构建XMLMapperBuilder来解析mapper文件,其实也不难理解,因为这是mybatis支持的两个地方可以使用mapper标签来定义mapper映射文件,具体解析代码当然是一样的逻辑。那我们解析来重点把目光投向XMLMapperBuilder。

2.2 XMLMapperBuilder

XMLMapperBuilder#parse

public void parse() {

if (!configuration.isResourceLoaded(resource)) { // @1

configurationElement(parser.evalNode(“/mapper”));

configuration.addLoadedResource(resource);

bindMapperForNamespace();

}

parsePendingResultMaps(); // @2

parsePendingChacheRefs(); // @3

parsePendingStatements(); // @4

}

代码@1:如果该映射文件(*.Mapper.xml)文件未加载,则首先先加载,完成xml文件的解析,提取xml中与mybatis相关的数据,例如sql、resultMap等等。

代码@2:处理mybatis xml中ResultMap。

代码@3:处理mybatis缓存相关的配置。

代码@4:处理mybatis statment相关配置,这里就是本篇关注的,Sql语句如何与Mapper进行关联的核心实现。

接下来我们重点探讨parsePendingStatements()方法,解析statement(对应SQL语句)。

2.2.1 XMLMapperBuilder#parsePendingStatements

private void parsePendingStatements() {

Collection incompleteStatements = configuration.getIncompleteStatements();

synchronized (incompleteStatements) {

Iterator iter = incompleteStatements.iterator(); // @1

while (iter.hasNext()) {

try {

iter.next().parseStatementNode(); // @2

iter.remove();

} catch (IncompleteElementException e) {

// Statement is still missing a resource…

}

}

}

}

代码@1:遍历解析出来的所有SQL语句,用的是XMLStatementBuilder对象封装的,故接下来重点看一下代码@2,如果解析statmentNode。

2.2.2 XMLStatementBuilder#parseStatementNode

public void parseStatementNode() {

String id = context.getStringAttribute(“id”); // @1 start

String databaseId = context.getStringAttribute(“databaseId”);

if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;

Integer fetchSize = context.getIntAttribute(“fetchSize”);

Integer timeout = context.getIntAttribute(“timeout”);

String parameterMap = context.getStringAttribute(“parameterMap”);

String parameterType = context.getStringAttribute(“parameterType”);

Class<?> parameterTypeClass = resolveClass(parameterType);

String resultMap = context.getStringAttribute(“resultMap”);

String resultType = context.getStringAttribute(“resultType”);

String lang = context.getStringAttribute(“lang”);

LanguageDriver langDriver = getLanguageDriver(lang);

最后

看完美团、字节、腾讯这三家的面试问题,是不是感觉问的特别多,可能咱们又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。

开篇有提及我可是足足背下了1000道题目,多少还是有点用的呢,我看了下,上面这些问题大部分都能从我背的题里找到的,所以今天给大家分享一下互联网工程师必备的面试1000题

注意不论是我说的互联网面试1000题,还是后面提及的算法与数据结构、设计模式以及更多的Java学习笔记等,皆可分享给各位朋友

最新“美团+字节+腾讯”一二三面问题,挑战一下你能走到哪一面?

互联网工程师必备的面试1000题

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题

最新“美团+字节+腾讯”一二三面问题,挑战一下你能走到哪一面?

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

[外链图片转存中…(img-sObJbNto-1715438526302)]

互联网工程师必备的面试1000题

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题

[外链图片转存中…(img-tChsST8M-1715438526302)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值