mybatis 源码学习之getMapper过程分析

mybatis 源码学习之getMapper过程分析

一、 简介

      这篇文章分析mybatis3.2.8中getMapper的过程,分为两个部分,一是剖析mybatis初始化的过程(这里采用加载xml配置文件的方式)

二、 mybatis初始化

1. 配置文件

首先我们看下这里使用的配置文件,在mappers标签中利用class的形式配置(如果不清楚的话,可以戳下这)。



<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC">transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver">property>
                <property name="url" value="jdbc:mysql://localhost:3306/test2">property>
                <property name="username" value="root">property>
                <property name="password" value="root">property>
            dataSource>
        environment>
    environments>
    <mappers>
        <mapper class="com.hwb.study.smybatis.mapper.EmployeesMapper">mapper>
    mappers>
configuration> 

2. 初始化

这里利用以上配置文件进行初始化

public static SqlSessionFactory buildSqlSessionFactory() throws IOException {
        // 配置文件
        String resource = "mybatis-config.xml";
        // 讲配置文件转化为输入流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 利用SqlSessionFactoryBuilder进行初始化
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }

简单介绍SqlSessionFactoryBuilder,来看一下它的源码

public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
    }

    ...

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

    ...
}

SqlSessionFactoryBuilder 采用builder设计模式,它的作用就是为了帮助生成sqlsessionfactory,关键还是利用XMLConfigBuilder类来真正解析Xml配置文件,返回sqlsessionfactory,这里的关键代码就是

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

接下来我们可以看下XMLConfigBuilder的源码

public class XMLConfigBuilder extends BaseBuilder {
    ...
    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }
    ...
    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }
    ...
    public Configuration parse() {
        if (parsed) {
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
      }

    private void parseConfiguration(XNode root) {
        try {
          Properties settings = settingsAsPropertiess(root.evalNode("settings"));
          //issue #117 read properties first
          propertiesElement(root.evalNode("properties"));
          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"));
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
}

其中XPathParser工具类作用是利用javax.xml中工具类对xml配置文件进行解析(这个javax.xml的类大家可以自行百度)。在完成XMLConfigBuilder的实例化之后,关键来了,在XMLConfigBuilder的parse的方法中,在这个方法中解析xml中对mapper的配置。

mapperElement(root.evalNode("mappers"));

接下来我们看这个mapperElement()方法;

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            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.");
          }
      }
   }
}

在这段代码中,最后一个else if(resource = = null && url = = null && mapperClass != null),这里就是判断mapper是以那种方式定义,我们的配置文件中以class(interface)的方式配置,所以会进入到这个代码块中。这里的重点就是 configuration.addMapper(mapperInterface); 将这个配置加载如configuration中的mapperRegistry。至此基本的Mapper解析算是完成了。接下来我们看看sqlsession是如何获取已经注册好的mapper的。

三、getMapper 剖析

从这里开始分析sqlsession.getMapper的过程。经过第二步中的初始化之后,我们可以得到SqlSessionFactory,然后利用其创建一个sqlsession,利用这个sqlsession获得其mapper的代理类。代码如下

/**
     * 研究mybatis mapper 过程
     *
     * @throws IOException
     */
    public static void testGetMapper() throws IOException {
        SqlSessionFactory factory = buildSqlSessionFactory();
        SqlSession sqlSession = factory.openSession();

        // get mapper
        EmployeesMapper mapper = sqlSession.getMapper(EmployeesMapper.class);

        System.out.println(mapper.getClass());

        // bulid param
        Map paramMap = new HashMap();
        paramMap.put("employee_id", 100);

        // execute query
        List> list = mapper.selectByPrimary(paramMap);

        // print result
        System.out.println(list);

        sqlSession.close();
    }

我们进入到sqlsession.getmapper的源码中,能够发现其实它只是调用了其内部的configuration.getMapper()方法。

    return configuration.getMapper(type, this);
  }```
  回顾一下第二步中的最后,mapperElement解析方法中对class类型的mapper的处理,

if (resource == null && url == null && mapperClass != null) {
Class

最后

关于configuration 中管理mapper的MapperRegistry流程,之后我会再写一篇博客,继续学习!!

你可能感兴趣的:(mybatis)