博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis 源码解析 -- 基于配置的源码解析(二)
阅读量:6199 次
发布时间:2019-06-21

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

  hot3.png

mapper解析

接着上篇的配置,本篇主要讲解mappers标签

该标签的主要功能:标记Mybatis数据库接口或Mybatis映射文件,配置方式有三:

数据库接口所在父包路径
数据库接口类路径
数据库映射文件路径
数据库映射文件资源路径

源码解析:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode root)

private void parseConfiguration(XNode root) {    try {      //issue #117 read properties first      propertiesElement(root.evalNode("properties"));      Properties settings = settingsAsProperties(root.evalNode("settings"));      loadCustomVfs(settings);      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      reflectorFactoryElement(root.evalNode("reflectorFactory"));      settingsElement(settings);      // read it after objectFactory and objectWrapperFactory issue #631      environmentsElement(root.evalNode("environments"));      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      // mappers 标签解析      mapperElement(root.evalNode("mappers"));    } catch (Exception e) {      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);    }  }

进入到方法

private void mapperElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) { // 解析 mappers 的子节点        if ("package".equals(child.getName())) { // 配置接口扫描包 package 节点(扫描包)          String mapperPackage = child.getStringAttribute("name");          configuration.addMappers(mapperPackage); // 如果配置了package节点,mybatis默认会将该包下所有的接口类加载到mybatis中        } else { // 单独配置          String resource = child.getStringAttribute("resource");          String url = child.getStringAttribute("url");          String mapperClass = child.getStringAttribute("class");          if (resource != null && url == null && mapperClass == null) { // resource xml 文件            ErrorContext.instance().resource(resource);            InputStream inputStream = Resources.getResourceAsStream(resource);            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());            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."); } } } } }

得到mappers节点后,解析其子节点。

如果子节点中存在 package 节点,则解析方式如下:

  1. 获取包路径 name属性值
  2. 解析:configuration.addMappers(mapperPackage);
    public void addMappers(String packageName) {    mapperRegistry.addMappers(packageName);  }  /**   * @since 3.2.2 也就是mybatis3.2.2版本才增加了这一功能   */  public void addMappers(String packageName) {    // 添加mappers,并指定要添加的类或子类的类型    addMappers(packageName, Object.class);  }  /**   * @since 3.2.2   */  public void addMappers(String packageName, Class
    superType) { // 工具类:用来判断是否是指定类或其子类,并存储结果 ResolverUtil
    > resolverUtil = new ResolverUtil
    >(); // 查找类 resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set
    >> mapperSet = resolverUtil.getClasses(); for (Class
    mapperClass : mapperSet) { addMapper(mapperClass); } } public ResolverUtil
    find(Test test, String packageName) { // 获取包路径 String path = getPackagePath(packageName); try { // 获取包路径下的所有文件路径(格式:com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.class) List
    children = VFS.getInstance().list(path); // 遍历包路径下的所有文件路径 for (String child : children) { // 过滤 class 文件 if (child.endsWith(".class")) { // 判断是否是指定类或指定类的子类 addIfMatching(test, child); } } } catch (IOException ioe) { log.error("Could not read package: " + packageName, ioe); } return this; } // 所以包路径的格式类似:com.xxx.xxx.xxx protected String getPackagePath(String packageName) { return packageName == null ? null : packageName.replace('.', '/'); } // 判断是否是指定类或指定类的子类 protected void addIfMatching(Test test, String fqn) { try { String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.'); ClassLoader loader = getClassLoader(); if (log.isDebugEnabled()) { log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]"); } // 得到类的Class类型 Class
    type = loader.loadClass(externalName); // 判断,符合存储到set集合中 if (test.matches(type)) { matches.add((Class
    ) type); } } catch (Throwable t) { log.warn("Could not examine class '" + fqn + "'" + " due to a " + t.getClass().getName() + " with message: " + t.getMessage()); } } // 添加 public
    void addMapper(Class
    type) { if (type.isInterface()) { // 首先是接口 if (hasMapper(type)) { // 判断是否已添加过 throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // 缓存接口及其代理 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. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }

    分析上面的代码:a、根据包名称转换得到包路径;b、根据包路径获取该包下所有的文件;c、遍历文件,取出class结尾的文件;d、判断是否是指定类或指定类的子类;e:添加并解析

  3. 具体的解析(非package节点的解析直接进入这):

    public 
    void addMapper(Class
    type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { 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. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }

    分析:1、首先是接口;2、之前未添加过haddMapper;3、存储:knownMappers.put(type, new MapperProxyFactory<T>(type));4、映射文件解析

  4. 映射文件解析

    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();
    public MapperAnnotationBuilder(Configuration configuration, Class
    type) { String resource = type.getName().replace('.', '/') + ".java (best guess)"; this.assistant = new MapperBuilderAssistant(configuration, resource); this.configuration = configuration; this.type = type; sqlAnnotationTypes.add(Select.class); sqlAnnotationTypes.add(Insert.class); sqlAnnotationTypes.add(Update.class); sqlAnnotationTypes.add(Delete.class); sqlProviderAnnotationTypes.add(SelectProvider.class); sqlProviderAnnotationTypes.add(InsertProvider.class); sqlProviderAnnotationTypes.add(UpdateProvider.class); sqlProviderAnnotationTypes.add(DeleteProvider.class); }
    public void parse() {    String resource = type.toString();    if (!configuration.isResourceLoaded(resource)) { // 判断是否解析过      loadXmlResource(); // 解析映射文件      configuration.addLoadedResource(resource); // 添加已解析      assistant.setCurrentNamespace(type.getName()); // 校验映射文件命名空间      parseCache(); // 缓存解析      parseCacheRef(); // TODO      Method[] methods = type.getMethods(); // 获取接口的所有方法      for (Method method : methods) { // 遍历        try {          // issue #237          if (!method.isBridge()) { //             parseStatement(method); //           }        } catch (IncompleteElementException e) {          configuration.addIncompleteMethod(new MethodResolver(this, method));        }      }    }    parsePendingMethods(); //  }

    映射文件解析:loadXmlResource();

  5. private void loadXmlResource() {    // Spring may not know the real resource name so we check a flag    // to prevent loading again a resource twice    // this flag is set at XMLMapperBuilder#bindMapperForNamespace    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {      // 映射文件默认和数据库接口文件在同一包下      String xmlResource = type.getName().replace('.', '/') + ".xml";      InputStream inputStream = null;      try {        inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);      } catch (IOException e) {        // ignore, resource is not required      }      if (inputStream != null) {        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());        xmlParser.parse();      }    }  }
    XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());xmlParser.parse();

     

转载于:https://my.oschina.net/asddsa/blog/1573374

你可能感兴趣的文章
oracle开发之<<SQL Cookbook>>学习笔记整理:第三章 操作多个表
查看>>
玩转Vuejs--异步队列那点事
查看>>
文本域(input file)上传文件的深入探究
查看>>
Big Number-Asia 2002, Dhaka (Bengal) (计算位数)题解
查看>>
对话Spring.NET
查看>>
Linux 系统出问题,怎么恢复EXT3/EXT4格式数据
查看>>
团队任务2
查看>>
杭电1284--钱币兑换问题(有趣)
查看>>
spring、struts、mybatis、Postgresql集成-使用存储过程进行分页
查看>>
Linux下如何查看哪些端口处于监听状态
查看>>
CF Gym 100637J Superfactorial numeral system (构造)
查看>>
linux目录详细介绍
查看>>
Java 对称数据加密AES
查看>>
MongoDB分布式集群搭建(分片加副本集)
查看>>
lambda 表达式树
查看>>
day5 笔记
查看>>
安装最新Nginx
查看>>
周末大放送网站图片上传,水印,预览,截图
查看>>
【BZOJ 2654】tree
查看>>
Webpack 2 设置为从当前文件夹逐级向上查找模块
查看>>