maybatis源码分析(二)——mybatis mapper的注册流程

 

mybatis mapper的注册一共分为两个 阶段:初始化 knownMappers,向knownMappers中put数据

1.初始化 knownMappers

前置条件如下(测试代码):

@BeforeAll
static void setUp() throws Exception {
  // create a SqlSessionFactory
  try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  }

执行步骤:

第一步:

 

 第二步:加载配置文件

第三步:构建XMLConfigBuilder

 第四步:构建Configuration对象

第五步:构造MapperRegistry对象

 

 第六步:构建knownMappers对象

 

2.向knownMappers中put数据

 具体代码如下:

第一步:解析配置文件中的configuration标签

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

 第二步:解析配置文件中的mappers标签

private void parseConfiguration(XNode root) {
  try {
    // issue #117 read properties first
    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);
    // read it after objectFactory and objectWrapperFactory issue #631
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
       //-------------解析配置文件中的mappers标签-----------//
    mapperElement(root.evalNode("mappers"));
       //-------------解析配置文件中的mappers标签-----------//
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

第三步:判断是是否配置了package标签

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");
        //----如果配置了package标签那就直接通过name属性去注册mapper-----//
        configuration.addMappers(mapperPackage);
      } else {
        ......
     }

(通过package去注册mapper比通过namespace简单,我们主要讲以namespace去注册)

否则走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);
    //根据配置文件去加载mapper文件
    try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
      XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
        //通过此处去注册mapper
      mapperParser.parse();
    }
  } else if (resource == null && url != null && mapperClass == null) {
    ErrorContext.instance().resource(url);
    try(InputStream inputStream = Resources.getUrlAsStream(url)){
      XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
     //通过此处去注册mapper
      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.");
  }
}

一般我们会在配置文件中配置如下代码:

<mappers>
    <mapper resource="org/apache/ibatis/autoconstructor/AutoConstructorMapper.xml" />
</mappers>

 

(当然mapper中还有两个属性:url 和class  url 主要是通过远程去联网读取文件并注册,calss则是直接通过你给的路径去加载类,其实他们主要的目的就一个 找到与xml文件对应的mapper接口并转化为MapperProxyFactory并将其注册到knowmappers中)

此时mybati会先根据我们配置的 resource路径去加载该配置文件,然后解析其中的namespace属性,通过此属性去找到对应的接口,进而完成mapper的注册。

具体流程如下:

加载mapper文件:

//根据配置文件去加载mapper文件
try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
  XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
//解析mapper文件
  mapperParser.parse();
}

解析mapper文件:

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
      //通过此处去注册mapper
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

 

注册mapper(关键代码已有中文注释):

private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      //通过类的全路劲名去加载类
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      // ignore, bound type is not required
    }
    if (boundType != null && !configuration.hasMapper(boundType)) {
      // Spring may not know the real resource name so we set a flag
      // to prevent loading again this resource from the mapper interface
      // look at MapperAnnotationBuilder#loadXmlResource
      configuration.addLoadedResource("namespace:" + namespace);
      //注册mapper
      configuration.addMapper(boundType);
    }
  }
}

 

 此时我们可以走到注册mapper的最终的地方:

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
       //------------------------最终注册的是该mapper的代理工厂类--------//
      //注入该接口对应的代理工厂类
      knownMappers.put(type, new MapperProxyFactory<>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

最终注册的是该mapper的代理工厂类

至此便完成了mapper的注册。 

总结一下:mapper注册其实他们主要的目的就一个 找到与xml文件对应的mapper接口并转化为MapperProxyFactory并将其注册到knowmappers中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值