其他分享
首页 > 其他分享> > Mybatis为什么只有mapper接口没有实现类

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

作者:互联网

  做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