Mybatis系列之MapperRegister

2019-07-16  本文已影响0人  超人也害羞

一、思路

  1. MapperRegister初始化过程;
  2. MapperRegister使用过程;

二、MapperRegister初始化过程

我们依然以Mybatis结合Spring的背景来分析MapperRegister。首先回顾一下之前SqlSessionFactory初始化过程,我们分析mybats-spring-1.2.5-sources.jar包中初始化代码。

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
  //  ...省略部分代码
  if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
        }
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }
    //  ...省略部分代码    
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

关注其中的xmlConfigBuilder.parse();这里就开始对XX-Mapper.XML的内容进行解析,往下走。

private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      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);
    }
  }

这里就是解析整个XML Config文件,其中的mapperElement(root.evalNode("mappers"));就是对我们定义的XX-Mapper.XML进行解析;

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

解析出接口名后就将该接口注册到configuration中的MapperRegister中,configuration.addMappers(mapperPackage),最终还是调用了MapperRegister中的AddMapper(...)方法。
至此,MapperRegister的初始化流程就简单介绍到这里,下面我们看一下MapperRegister的使用。


三、MapperRegister使用过程

MapperRegister使用过程主要是生成对应MapperProxy实例交给Spring进行管理。我们以一个Spring获取Bean的例子来讲解MapperRegister在其中起到了怎样的作用。

这段代码中,我们主要是解析了spring配置文件后,使用ApplicationContext来获取PersonMapper这个bean;

ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring.xml");
PersonMapper mapper = (PersonMapper) context.getBean("personMapper");

一路debug跟踪下去,我们会发现最终是调用MapperFactoryBean这个对象。(这里就忽略spring的过程了,关注点在mybatis上)

/**
   * {@inheritDoc}
   */
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

继续往下,spring使用的SqlSession是SqlSessionTemplate,我们再看看SqlSessionTemplate中是怎么实现的。

/**
   * {@inheritDoc}
   */
  @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

发现最终还是调用的Configuration的getMapper方法。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

继续看,我们发现最终还是调用了MapperRegister自己的getMapper方法。

public class MapperRegistry {

  private Configuration config;
  // 之前xmlConfigBuilder.parse()解析完成注册的对象。
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null)
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    try {
      // 使用jdk的动态代理方法生成MapperProxy代理对象。
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  // ... 省略部分代码
}

最终我们看到了还是调用了JDK自身的动态代理方法来生成一个MapperProxy代理对象。


好了,今天就到这了~~晚安

上一篇下一篇

猜你喜欢

热点阅读