【Mybatis】【配置文件解析】【四】Mybatis源码解析-mappers的解析四(绑定Mapper、处理未解析全的)
1 前言
我们上节把我们 mapper 里的 sql节点以及我们的增删改查都解析了,那么最后回来就剩下两块没看了,一块是我们的 mapper 跟我们的接口绑定,一块就是我们在解析的过程中报错的会放进未完全的集合中,这里来做最后的努力。那我们来看下这两块的东西。
2 源码分析
2.1 接口绑定 Mapper
从我们的 bindMapperForNamespace() 方法开始看起:
private void bindMapperForNamespace() { // 获取我们 Mapper 上的名称空间 我们不都是写的接口的全类名么 String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { // 根据全类名得到我们的 Mapper 接口 boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { // ignore, bound type is not required } // 检测当前 mapper 类是否被绑定过 if (boundType != null && !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); // 绑定 mapper 类 configuration.addMapper(boundType); } } }
// Configuraion public <T> void addMapper(Class<T> type) { // 通过 MapperRegistry 绑定 mapper 类 mapperRegistry.addMapper(type); }
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { /* * 将 type 和 MapperProxyFactory 进行绑定, * MapperProxyFactory 来为 mapper 接口生成代理类 */ knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. // 创建注解解析器。在 MyBatis 中,有 XML 和 注解两种配置方式可选 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); // 解析注解中的信息 parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
可以看到它创建代理,只是把我们的 mapper 接口作为属性包了一层,并没有创建真实的对象,至于什么时候创建,我们后续再说哈,我们解析阶段先只看静态的东西哈。对于Mapper 接口的绑定过程。这里简单一下:
- 获取命名空间,并根据命名空间解析 mapper 类型;
- 将 type 和 MapperProxyFactory 实例存入 knownMappers 中;
- 创建注解解析器进行解析(这个就不看了,注解无非也是拿到我们的注解的 sql 最后都是字符串 然后进行解析的。
2.2 处理未完整的对象
我们在解析过程中,会看到报错捕获到就会把它放进未完整的集合里,放的都是什么呢,都是不同的解析器对象,不知道你注意到没。在解析 resultMap时,有对应的 ResultMapResolver,解析 cache-ref 有对应的 CacheRefResolver,存放的都是其对用的解析器。出现未完整的对象的一个原因可能是每个 mapper 的加载顺序的不固定,比如会依赖某个 mapper 的东西但是对应的 mapper 还未加载,就会出现这样的情况。其实这三个的处理基本都是一样的,我们看其中一个:
private void parsePendingCacheRefs() { // 获取 CacheRefResolver 列表 Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs(); synchronized (incompleteCacheRefs) { // 迭代器遍历 Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator(); while (iter.hasNext()) { try { // 尝试再次解析 iter.next().resolveCacheRef(); // 解析成功删除自己,继续下一个 iter.remove(); } catch (IncompleteElementException e) { // 不做处理 // Cache ref is still missing a resource... } } } }
3 小结
好那么到这里为止,我们的 mapper 的解析过程就结束了,我们的配置解析也就结束了,有不对的地方欢迎指正哈。