【MyBtais源码阅读】(三)探究Configuration 2

        前面我们已经知道了Configuration 是在实例化XMLConfigBuilder的时候在构造函数里面一起实例化的,如下;

  //外层调用的public的构造函数
  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  //内部private的构造函数,被上述调用
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //此处实例化Configuration
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

        于是,我们又有了一个疑问,刚刚加载的stream流并没有传入这个new Configuration()里面,而是在外层构造函数里面,实例化XPathParser的时候传入了,此刻我猜想,这个new Configuration()里面肯定没有加载配置文件的内容,我们来一起看一下下面代码

public class Configuration {
    ......
      protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    ......

    //省略很多属性和方法

    ......
  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
    ......

}


public class TypeAliasRegistry {

  private final Map<String, Class<?>> typeAliases = new HashMap<>();

  
  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    registerAlias("ResultSet", ResultSet.class);
  }

......

  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    typeAliases.put(key, value);
  }
......
}

        在上述代码中,我们看到Configuration 构造函数里面全部都在一件事情,就是注册别名而且Configuration里面typeAliasRegistry属性在实例化的时候也注册了很多默认的别名。正如我们所猜想一样,实例化Configuration并没有加载配置文件,而仅仅只做了注册一系列默认的别名。但是我现在想找到我们加载的配置文件在何处解析成了Configuration里面的属性呢。我们回头看看刚刚所带疑问的XPathParser,因为实例化这个对象的时候是传入了流的。

public class XPathParser {

  private final Document document;
  private boolean validation;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;


  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(inputStream));
  }

  //代码1
  private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }

  //代码2
  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
          // NOP
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

//省略很多方法
......
}

        在调用的XPathParser构造函数里面,调用两个方法,一个通用的方法(代码1)分别填充了validation、entityResolver、variables和xpath(此处xpath是用来解析xml文件的)。而在另一个方法(代码2)处则是把传入的stream流转成了Document对象。这里转成Document和实例化xpath,让我们联想到了,后续肯定是需要通过xpath来解析这个Document了。因为XPathParser里面存储了xpath和Document,并且XMLConfigBuilder里面存储了XPathParser和Configuration。就在此处Configuration正式和加载的文件关联起来了,此刻XMLConfigBuilder已经实例化完成了,但是我们还是没看到解析的方法,我们只能回到SqlSessionFactoryBuilder.build 方法了

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }


public class XMLConfigBuilder extends BaseBuilder {
......
  public Configuration parse() {
    //判断是否已经解析过了,避免多次解析
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //解析之前设置解析标识为已经解析过
    parsed = true;
    //parser.evalNode("/configuration") 通过xpath的方式,解析从根节点开始的configuration节点
    //解析文档成为configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
......
}

        在build方法中,我们发现实例化XMLConfigBuilder 后,直接调用了一个parse方法,在方法里面又通过XPathParser调用evalNode,而实际上这个方法里面就是调用了xpath.evaluate,而这个方法正是通过xpath的方式解析最开始加载并且储存在XPathParser里面的document,返回一个XNode,在XNode 里面包含了XPathParser和通过xpath解析的根结点Node,实际上后面很多地方都会通过返回的XNode里面包含的XPathParser调用evalNode的方式来解析根结点Node,后面我们会详细看到

  
  //外层调用
  //代码a
  public XNode evalNode(String expression) {
    return evalNode(document, expression);
  }


  //代码b
  //被代码a调用
  public XNode evalNode(Object root, String expression) {
    Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
    if (node == null) {
      return null;
    }
    return new XNode(this, node, variables);
  }

  //代码c
  //被代码b调用
  private Object evaluate(String expression, Object root, QName returnType) {
    try {
      return xpath.evaluate(expression, root, returnType);
    } catch (Exception e) {
      throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
    }
  }

后面我们来具体看看parseConfiguration方法是如何解析的

总结:

        1:在build方法调用的时候,实例化了XMLConfigBuilder

        2:在XMLConfigBuilder里面实例化XPathParser和Configuration

        3:XPathParser实际上是包含了一个xpath和document的实例

        4:使用XPathParser解析文档的时候是通过xpath表达式的方式,使用内部的xpath加上传入的表达式来解析内部的document

        5:在解析configuration之前会先判断是否已经做过解析,避免多次解析文档

        6:解析完根结点返回XNode后,里面包含了XPathParser和已经解析过的根结点Node,后续解析都是基于在根结点Node上进行的

上一篇

【个人阅读源码,能力有限,望各方大佬有幸瞧见后能指点一二,如果有错误之处,我看到后会立马改正,谢谢🙏🙏🙏】

阿里云服务器优惠卷

阿里云优惠卷

阿里云新用户优惠

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值