博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis源码解析之Configuration加载(五)
阅读量:5339 次
发布时间:2019-06-15

本文共 10128 字,大约阅读时间需要 33 分钟。

概述

前面几篇文章主要看了mybatis配置文件configuation.xml中<setting>,<environments>标签的加载,接下来看一下mapper标签的解析,先来看下标签的配置:

1     
2
3
4 5
6
7 8
9
10

常用的主要就有上面三种方式,指定mapper的xml文件,指定package,指定mapper文件,我们来具体解析下。

解析

我们看下具体开始解析的代码:

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

这段代码就是针对上面不同的配置进行解析加载,这里面还有一种url的情况,是通过url加载外部的资源,不常用。

我们先来看配置了package的情形,

第5行代码,获取包名,第6行代码调用configuration的addMappers方法,具体看下:

1   public void addMappers(String packageName) {2     mapperRegistry.addMappers(packageName);3   }

configuation中又调用了mapperRegistry的addMappers方法:

1   public void addMappers(String packageName) {2     addMappers(packageName, Object.class);3   }

在看下addMappers方法:

1   public void addMappers(String packageName, Class
superType) {2 ResolverUtil
> resolverUtil = new ResolverUtil
>();3 resolverUtil.find(new ResolverUtil.IsA(superType), packageName);4 Set
>> mapperSet = resolverUtil.getClasses();5 for (Class
mapperClass : mapperSet) {6 addMapper(mapperClass);7 }8 }

 上面这段代码主要就是利用ResolverUtil遍历package下面的所有class文件,看下find方法:

1   public ResolverUtil
find(Test test, String packageName) { 2 String path = getPackagePath(packageName); 3 4 try { 5 List
children = VFS.getInstance().list(path); 6 for (String child : children) { 7 if (child.endsWith(".class")) { 8 addIfMatching(test, child); 9 }10 }11 } catch (IOException ioe) {12 log.error("Could not read package: " + packageName, ioe);13 }14 15 return this;16 }

很明显,上面就是根据包名获取下面的class,然后调用addIfMatching方法:

1   protected void addIfMatching(Test test, String fqn) { 2     try { 3       String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.'); 4       ClassLoader loader = getClassLoader(); 5       if (log.isDebugEnabled()) { 6         log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]"); 7       } 8  9       Class
type = loader.loadClass(externalName);10 if (test.matches(type)) {11 matches.add((Class
) type);12 }13 } catch (Throwable t) {14 log.warn("Could not examine class '" + fqn + "'" + " due to a " +15 t.getClass().getName() + " with message: " + t.getMessage());16 }17 }

这个方法主要就是根据前面传过来的class,去加载这个类,注意这边用的类加载器是线程上下文类加载器,然后判断这个类的类型是不是Object类,是的话,就放入matches这个set中,都遍历完成之后,符合要求的class就在这个set中,怎么获取呢?看下getClass方法:

1   public Set
> getClasses() {2 return matches;3 }

这边调用getClass方法时就返回了这个Set,我们接着看mapperRegistry的addMappers方法,

1   public void addMappers(String packageName, Class
superType) {2 ResolverUtil
> resolverUtil = new ResolverUtil
>();3 resolverUtil.find(new ResolverUtil.IsA(superType), packageName);4 Set
>> mapperSet = resolverUtil.getClasses();5 for (Class
mapperClass : mapperSet) {6 addMapper(mapperClass);7 }8 }

按照上面的分析,第4行代码,我们已经得到的复合条件的class,下面就是遍历这些class,调用addMapper方法:

1   public 
void addMapper(Class
type) { 2 if (type.isInterface()) { 3 if (hasMapper(type)) { 4 throw new BindingException("Type " + type + " is already known to the MapperRegistry."); 5 } 6 boolean loadCompleted = false; 7 try { 8 knownMappers.put(type, new MapperProxyFactory
(type)); 9 // It's important that the type is added before the parser is run10 // otherwise the binding may automatically be attempted by the11 // mapper parser. If the type is already known, it won't try.12 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);13 parser.parse();14 loadCompleted = true;15 } finally {16 if (!loadCompleted) {17 knownMappers.remove(type);18 }19 }20 }21 }

这段代码首先判断这个class的类型是不是接口,不是则直接跳过,是的话,判断这个class对应的mapper文件是不是已经加载了,是的话,则抛出异常,否则看下第8行代码,将当前class类作为key,new一个MapperProxyFactory作为value放入knownMappers这个map中,这边这个MapperProxyFactory我们后面会详细介绍,看第12行代码,这边new了一个MapperAnnotationBuilder,看下第13行代码,调用了MapperAnnotationBuilder的parse方法:

1   public void parse() { 2     String resource = type.toString(); 3     if (!configuration.isResourceLoaded(resource)) { 4       loadXmlResource(); 5       configuration.addLoadedResource(resource); 6       assistant.setCurrentNamespace(type.getName()); 7       parseCache(); 8       parseCacheRef(); 9       Method[] methods = type.getMethods();10       for (Method method : methods) {11         try {12           // issue #23713           if (!method.isBridge()) {14             parseStatement(method);15           }16         } catch (IncompleteElementException e) {17           configuration.addIncompleteMethod(new MethodResolver(this, method));18         }19       }20     }21     parsePendingMethods();22   }

这边我们只要关注第4行代码,第5行代码之后都是针对注解做的处理,具体看下:

1   private void loadXmlResource() { 2     // Spring may not know the real resource name so we check a flag 3     // to prevent loading again a resource twice 4     // this flag is set at XMLMapperBuilder#bindMapperForNamespace 5     if (!configuration.isResourceLoaded("namespace:" + type.getName())) { 6       String xmlResource = type.getName().replace('.', '/') + ".xml"; 7       InputStream inputStream = null; 8       try { 9         inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);10       } catch (IOException e) {11         // ignore, resource is not required12       }13       if (inputStream != null) {14         XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());15         xmlParser.parse();16       }17     }18   }

这边首先再次确认xml文件有没有被加载过,没有的话继续加载,在当前目录下根据mapper的名字寻找xml文件,然后加载成流,之后交给XmlMapperBuilder处理,其实到这边,xml就已经加载完成了,后续都是处理一些使用了注解的东西,使用注解我们暂且不看。

我们再来看下方法三:

mapperClass不为空,走第三个if:

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

第22行代码,根据名称加载mapper类,然后调用configuation的addMapper方法,这个我们上面已经分析过了,其实这种情况跟用package的很相似,可以说是一种特殊情况,package里面会有很多的mapper。

我们再看下方法一:

resource不为空: 

1           if (resource != null && url == null && mapperClass == null) {2             ErrorContext.instance().resource(resource);3             InputStream inputStream = Resources.getResourceAsStream(resource);4             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());5             mapperParser.parse();6           }

 

这边跟上面那个部分一样的,我们下一篇文章就主要看下这个解析过程。

转载于:https://www.cnblogs.com/xiaobaobei/p/10367988.html

你可能感兴趣的文章
JS性能DOM优化
查看>>
设计模式 单例模式 使用模板及智能指针
查看>>
c#的const可以用于引用类型吗
查看>>
手动实现二值化
查看>>
What Linux bind mounts are really doing
查看>>
linux top命令详解
查看>>
博弈论小结
查看>>
模拟Post登陆带验证码的网站
查看>>
NYOJ458 - 小光棍数
查看>>
java中常用方法
查看>>
【Programming Clip】06、07年清华计算机考研上机试题解答(个别测试用例无法通过)...
查看>>
canvas动画
查看>>
4,7周围玩家
查看>>
关于webpack升级过后不能打包的问题;
查看>>
vue - 生命周期
查看>>
Python正则表达式
查看>>
Linux进程间通信--命名管道
查看>>
UVa 10970 - Big Chocolate
查看>>
js输出
查看>>
H5多文本换行
查看>>