分享

Mybatis源码详解

 我心永恒lz 2017-09-29


1.前言

Mybatis是当前最流行的持久层框架之一,其官方使用手册详见:http://www./mybatis-3/zh/index.html

使用Mybatis主要分为以下几个步骤:

1) 添加mybatis依赖到pom文件(maven项目)或jar包到项目中;

2) 添加mybatis-config.xml配置文件,包含的配置信息与配置方式详见使用手册;

3) 解析配置文件并创建Configuration对象configuration;

4) 使用configuration创建SqlSessionFactory对象;

5) 通过sqlSession获取mapper实例,并调用mapper接口中方法与DB交换。

下面就对上述步骤涉及到的主要源码进行分析讲解。


2.构造SqlSessionFactory实例

通过SqlSessionFactoryBuilder构造应用级别SqlSessionFactory实例,核心代码如下:

// inputStream,mybatis-config.xml配置文件的输入流

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 SqlSessionFactory build(Configuration config) {

    return new DefaultSqlSessionFactory(config);

  }

 

构造过程如下:

1)创建XMLConfigBuilder对象parser,同时创建了Configuration对象configuration;

2)调用parser.parser()解析mybatis配置文件,保持配置信息到configuration中;

3)创建SqlSessionFactory实例,即DefaultSqlSessionFactory对象。

 

3.解析mybatis配置文件

解析mybatis配置文件核心代码如下:

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 {

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

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

    } catch (Exception e) {

      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);

  }

}

 

parseConfiguration方法依次解析mybatis配置文件中各元素,具体解析过程如下所示:

propertiesElement,解析元素properties,保存在variables中;

typeAliasesElement,解析元素typeAliases,保存在typeAliasRegistry中;

pluginElement,解析插元素plugins,保存在interceptorChain中;

objectFactoryElement,解析元素objectFactory,保存在objectFactory中;

objectWrapperFactoryElement,解析元素objectWrapperFactory,保存在objectWrapperFactory中;

reflectorFactoryElement,解析元素reflectorFactory,保存在reflectorFactory中;

settingsElement,解析元素settings,保存在configuration属性中;

environmentsElement,解析元素environments,保存在environment中;

databaseIdProviderElement,解析元素databaseIdProvider,保存在databaseId中;

typeHandlerElement,解析元素typeHandlers,保存在typeHandlerRegistry中;

mapperElement,解析元素mappers,保存在mapperRegistry中。

 

4.解析mappers元素

4.1 mappers配置方式

mappers有四种配置方式,如下所示:

详细配置详见:http://www./mybatis-3/configuration.html#mappers

 

4.2解析mappers核心代码

private void mapperElement(XNode parent) throws Exception {

  if (parent != null) {

    for (XNode child : parent.getChildren()) {

      if ("package".equals(child.getName())) {

  // 对应配置方式4,查找属性name指定包下所有的接口类型,注册到mapperRegistry

        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) {

     // 对应配置方式1

          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) {

     // 对应配置方式2

          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) {

     // 对应配置方式3

          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的前两种配置解析步骤:

1) 创建XMLMapperBuilder对象mapperParser,调用mapperParser.parse()解析*mapper.xml文件;

2) mapper配置文件的namespace属性值为type,创建type对应的MapperProxyFactory,并注册mapperd到

mapperRegistry中。

mappers的后两种配置解析步骤:

1) mapper接口类型为type,创建type对应的MapperProxyFactory,并注册mapper到mapperRegistry中;

2) 创建XMLMapperBuilder对象mapperParser,调用mapperParser.parse()解析*mapper.xml文件。

 

4.3 MapperRegistry

MapperRegistry是mybatis中用于注册mapper带configuration的核心类,其源码如下:

public class MapperRegistry {

  private final Configuration config;

  private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();


  public MapperRegistry(Configuration config) {

    this.config = config;

  }

 

  // 当执行sqlSession.getMapper(Class type)方法时会调用该方法,

// 取出type对应的MapperProxyFactory对象,返回mapper的代理对象mapperProxy

  @SuppressWarnings("unchecked")

  public T getMapper(Class type, SqlSession sqlSession) {

    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);

    if (mapperProxyFactory == null) {

      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");

    }

    try {

      return mapperProxyFactory.newInstance(sqlSession);

    } catch (Exception e) {

      throw new BindingException("Error getting mapper instance. Cause: " + e, e);

    }

  }

 

  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 {

        // 以mapper类型为参数创建mapper的代理工厂对象,并将其保存,

        knownMappers.put(type, new MapperProxyFactory(type));

        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);

        parser.parse();

        loadCompleted = true;

      } finally {

        if (!loadCompleted) {

          knownMappers.remove(type);

        }

      }

    }

  }

 

  ......

}

 

5.MapperProxyFactory

MapperProxyFactory是MapperProxy的工厂类,其核心源码如下:

public class MapperProxyFactory {

  private final Class mapperInterface;

 

  // 构造函数接收mapper接口类型,用于创建mapper的代理对象mapperProxy

  public MapperProxyFactory(Class mapperInterface) {

    this.mapperInterface = mapperInterface;

  }

 

  @SuppressWarnings("unchecked")

  protected T newInstance(MapperProxy mapperProxy) {

    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

  }

 

// 该方法实际上就是创建mapperProxy的工厂方法

  public T newInstance(SqlSession sqlSession) {

    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);

    return newInstance(mapperProxy);

  }

 

  ......

}

 

6.MapperProxy

Mapper接口的jdk动态代理类,调用mapper接口方法会执行代理类invoke方法。

核心代码如下:

public class MapperProxy implements InvocationHandler, Serializable {

 

  private final SqlSession sqlSession;

  private final Class mapperInterface;

  private final Map methodCache;

 

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {

    this.sqlSession = sqlSession;

    this.mapperInterface = mapperInterface;

    this.methodCache = methodCache;

  }

 

  @Override

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    try {

      if (Object.class.equals(method.getDeclaringClass())) {

        return method.invoke(this, args);

      } else if (isDefaultMethod(method)) {

        return invokeDefaultMethod(proxy, method, args);

      }

    } catch (Throwable t) {

      throw ExceptionUtil.unwrapThrowable(t);

    }

final MapperMethod mapperMethod = cachedMapperMethod(method);

// 执行下面的方法,会调用sqlSession的insert、update、delete、

// select等方法,进而调用executor执行sql等操作

    return mapperMethod.execute(sqlSession, args);

  }

 

  ......

}

 

7.执行mapper接口方法

SqlSession session = sqlSessionFactory.openSession(true);

try {

  // mapper实际上是BlogMapper接口的代理对象mapperProxy

  BlogMapper mapper = session.getMapper(BlogMapper.class);

  // 实际上执行的时MapperProxy的invoke方法

  Blog blog = mapper.selectBlog(101);

} finally {

  session.close();

}

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多