ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Mybatis为什么只有mapper接口没有实现类

2019-08-03 23:04:05  阅读:330  来源: 互联网

标签:mapper getMapper 接口 new Mybatis org type throw


  做JAVA开发的小伙伴都知道,接口几乎都由实现类实现其功能,使用接口作变量引用实现类作变量实例。然而有部分接口我们在源代码中却找不到其实现类,mybatis的mapper接口便是如此。那么,他们是怎么实现其功能的呢,那就是动态代理。

什么是动态代理这里就不做解释了,不了解的朋友可以参考一下设计模式。

mybatis的动态代理过程:

  初始化SqlSessionFactory解析mapper.xml的namespace属性的时候,将MapperProxyFactory代理工厂存入mapper缓存中,源代码的调取过程如下:

org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory ->
if (this.mapperLocations.length == 0) {
  LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
  for (Resource mapperLocation : this.mapperLocations) {
    if (mapperLocation == null) {
      continue;
    }
    try {
      XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
      targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
      xmlMapperBuilder.parse();
    } catch (Exception e) {
      throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    } finally {
      ErrorContext.instance().reset();
    }
    LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
  }
}

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse ->
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

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

org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace ->
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) {
      if (!configuration.hasMapper(boundType)) {
        configuration.addLoadedResource("namespace:" + namespace);
        configuration.addMapper(boundType);
      }
    }
  }
}

org.apache.ibatis.session.Configuration#addMapper ->
public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

org.apache.ibatis.binding.MapperRegistry#addMapper ->
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 {
      knownMappers.put(type, new MapperProxyFactory<>(type));
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}


  注册bean(mapper)的时候会调用doGetObjectFromFactoryBean,这个时候FactoryBean<?>传入的是MapperFactoryBean对象,然后获取前面存入knownMappers里面的MapperProxyFactory代理工厂,用代理工创建一个Mapper代理实例给容器注册

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean ->
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {

  Object object;
  try {
    if (System.getSecurityManager() != null) {
      AccessControlContext acc = getAccessControlContext();
      try {
        object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
      }
      catch (PrivilegedActionException pae) {
        throw pae.getException();
      }
    } else {
      object = factory.getObject();
    }
  }
  catch (FactoryBeanNotInitializedException ex) {
    throw new BeanCurrentlyInCreationException(beanName, ex.toString());
  }
  catch (Throwable ex) {
    throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
  }

  if (object == null) {
    if (isSingletonCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(
        beanName, "FactoryBean which is currently in creation returned null from getObject");
    }
    object = new NullBean();
  }
  return object;
}

org.mybatis.spring.mapper.MapperFactoryBean#getObject ->
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}


org.mybatis.spring.SqlSessionTemplate#getMapper ->
public <T> T getMapper(Class<T> type) {
  return getConfiguration().getMapper(type, this);
}

org.apache.ibatis.session.Configuration#getMapper ->
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

org.apache.ibatis.binding.MapperRegistry#getMapper ->
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 {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

所以,注册到spring容器的mapper其实是MapperProxy,我们调用mapper接口的时候就会自动装配动态生成的MapperProxy实例实现mapper的功能。
第一次写博客,内容写得比较粗糙,有什么意见或者建议请建议博主Qq,谢谢。

 

标签:mapper,getMapper,接口,new,Mybatis,org,type,throw
来源: https://www.cnblogs.com/hx-dawn/p/11296650.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有