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接口没有实现类的主要内容,如果未能解决你的问题,请参考以下文章