打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
mybatis源码分析-配置文件解析过程

mybatis源码分析-环境搭建 一文中,我们的测试代码如下:

public static void main(String[] args) throws IOException {     String resource = "mybatis-config.xml";      InputStream inputStream = Resources.getResourceAsStream(resource);      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);      SqlSession sqlSession = sqlSessionFactory.openSession();      try {          DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);          List<Dept> deptList = deptMapper.getAllDept();          System.out.println(deptList);      } finally {          sqlSession.close();      }   }  

mybatis源码分析-SqlSessionFactory构建过程 一文中探究了 SqlSessionFactory 对象的生成方式,但是那里还有两行代码没有仔细研究,因为这两行代码涉及的东西有些多,这篇文章主要研究这两行代码背后的细节。代码再贴一遍:

  XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);    return build(parser.parse());  

XMLConfigBuilder做了什么

需要研究的第一行代码只是生成了一个 XMLConfigBuilder 对象而已。

public XMLConfigBuilder(InputStream inputStream) {      this((InputStream)inputStream, (String)null, (Properties)null);  }

继续看构造重载函数:

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {      this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);  }

这里继续调用了另一个构造函数,只是入参变为 XPathParser对象。

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {      super(new Configuration());      this.localReflectorFactory = new DefaultReflectorFactory();      ErrorContext.instance().resource("SQL Mapper Configuration");      this.configuration.setVariables(props);      this.parsed = false;      this.environment = environment;      this.parser = parser;  }

这个构造函数主要是赋值功能,给 XMLConfigBuilder 对象的属性赋值。注意 XMLConfigBuilder extends BaseBuilder ,而 BaseBuilder 含有 Configuration 属性 , 因此 XMLConfigBuilder 需要给一个默认的 Configuration 值,就是下面这行代码的功能:

super(new Configuration());  

看上面的流程,似乎也不复杂,这里漏了一点 XPathParser 的创建,只有创建好了 XPathParser 才能进行文件解析,然后生成对应的 Configuration 对象。

XPathParser 使用了 xPath 解析技术。

xml解析的技术有很多,以前用过 dom4j,当使用dom4j查询比较深的层次结构的节点(标签,属性,文本)时比较麻烦!使用xPath主要是用于快速获取所需的节点对象。

本文不打算讲解如何把 inputStream 转为 XPathParser 对象,有兴趣可以自学一下。

上面讲解的代码主要掌握创建 XMLConfigBuilder 对象时有一个特别重要的属性 XPathParser ,这个属性可以快速获取 xml 的各种元素,方便后续操作。

parser.parse() 方法做了什么?

根据上下文,这里的 parser 就是 XMLConfigBuilder,我们来看下 parse() 方法做了什么:

public Configuration parse() {      if (this.parsed) {          throw new BuilderException("Each XMLConfigBuilder can only be used once.");      } else {          this.parsed = true;          this.parseConfiguration(this.parser.evalNode("/configuration"));          return this.configuration;      }  }

注意这段代码的核心是:

this.parseConfiguration(this.parser.evalNode("/configuration"));  

注意this.parser 指的是 XMLConfigBuilder 里的 XPathParser 对象。evalNode 方法获取全局配置文件里面 Configuration 下面的所有内容。现在想想全局配置文件的结构吧。

再看下 parseConfiguration 方法:

private void parseConfiguration(XNode root) {    try {      propertiesElement(root.evalNode("properties"));      Properties settings = settingsAsProperties(root.evalNode("settings"));      loadCustomVfs(settings);      loadCustomLogImpl(settings);      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      reflectorFactoryElement(root.evalNode("reflectorFactory"));      settingsElement(settings);      environmentsElement(root.evalNode("environments"));      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      mapperElement(root.evalNode("mappers"));    } catch (Exception e) {      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " \+ e, e);    }  }

全局配置文件里面的每一个属性,都有对应的方法进行解析。解析成一个一个对象赋值给最大的那个 Configuration 对象,到此就完事了。

解析插件代码

上面的解析配置文件的方法很多,本文不会把所有的解析代码都一一探究,就使用插件解析代码进行举例说明吧。也就是下面这行代码:

    pluginElement(root.evalNode("plugins"));  

在研究源码之前,我们先了解下插件机制,下面的内容都是从官网复制的:


MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java  @Intercepts({@Signature(          type= Executor.class,          method = "update",          args = {MappedStatement.class,Object.class})})  public class ExamplePlugin implements Interceptor {      private Properties properties = new Properties();      public Object intercept(Invocation invocation) throws Throwable {          // implement pre processing if need          Object returnObject = invocation.proceed();          // implement post processing if need          return returnObject;      }      public void setProperties(Properties properties) {          this.properties = properties;      }  }

下面是如何配置:

<!-- mybatis-config.xml -->  <plugins>      <plugin interceptor="org.mybatis.example.ExamplePlugin">          <property name="someProperty" value="100"/>      </plugin>  </plugins>

现在我们想如何把上面配置文件的代码解析出来,源码如下:

private void pluginElement(XNode parent) throws Exception {      if (parent != null) {          for (XNode child : parent.getChildren()) {              String interceptor = child.getStringAttribute("interceptor");              Properties properties = child.getChildrenAsProperties();              Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();              interceptorInstance.setProperties(properties);              configuration.addInterceptor(interceptorInstance);          }      }  }

由于 plugin 可以有多个,因此代码循环解析,对于每一个 plugin,首先拿到属性 interceptor,也就是自定义插件的实现类,如上面官网的例子 ExamplePlugin ,通过反射生成对象实例,该对象有个属性 Properties,也就是 mybatis-config.xml 中的

 <property name="someProperty" value="100"/>  

内容,只不过被

child.getChildrenAsProperties()

进行解析成键值对形式的 Properties 对象,代码如下

public Properties getChildrenAsProperties() {      Properties properties = new Properties();      Iterator var2 = this.getChildren().iterator();        while(var2.hasNext()) {          XNode child = (XNode)var2.next();          String name = child.getStringAttribute("name");          String value = child.getStringAttribute("value");          if (name != null && value != null) {              properties.setProperty(name, value);          }      }        return properties;  }

这段代码比较简单,循环取 name 和 value 的值给 Properties 对象而已。

对于mybatis-config.xml其它配置,也是通过类似的方式解析成相关对象,最终都赋值给 Configuration对象而已。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Mybatis深入之初始化过程
Mybatis源码分析(四):属性接口之configLocation
Mybatis源码详解
mybatis源码分析(1)
设计模式系列 | 建造者模式
深入浅出MyBatis
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服