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

Posted hx-dawn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,谢谢。

 

以上是关于Mybatis为什么只有mapper接口没有实现类的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis Mapper接口是如何找到实现类的-源码分析

MyBatis的SQL映射文件

mybatis如何根据mapper接口生成其实现类

MyBatis接口的简单实现原理

MyBatis之sql映射文件

Mybatis实现Mapper动态代理方式