mybatis如何通过接口查找对应的mapper.xml及方法执行详解

Posted 虎子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis如何通过接口查找对应的mapper.xml及方法执行详解相关的知识,希望对你有一定的参考价值。

mybatis如何通过接口查找对应的mapper.xml及方法执行详解
主要给大家介绍了利用mybatis如何通过接口查找对应的mapper.xml及方法执行的相关资料,文中通过示例代码
介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着小编一起来学习学习吧。
本文主要介绍的是关于mybatis通过接口查找对应mapper.xml及方法执行的相关内容,下面话不多说,来看看详细的介绍:
在使用mybatis的时候,有一种方式是
BookMapper bookMapper = SqlSession().getMapper(BookMapper.class)
获取接口,然后调用接口的方法。只要方法名和对应的mapper.xml中的id名字相同,就可以执行sql。
那么接口是如何与mapper.xml对应的呢?
首先看下,在getMapper()方法是如何操作的。
在DefaultSqlSession.Java中调用了configuration.getMapper()

public <T> T getMapper(Class<T> type) 
return configuration.<T>getMapper(type, this);


在Configuration.java中调用了mapperRegistry.getMapper(type, sqlSession);

public <T> T getMapper(Class<T> type, SqlSession sqlSession) 
return mapperRegistry.getMapper(type, sqlSession);


下面重点来了,在MapperRegistry.java中实现了动态代理

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);


这个函数分两部分来看,首先是从map集合中获取接口代理,map集合的来源,第二部分获取代理后实例化,获取接口的方
法,执行sql。
对于第一部分:集合的来源。
这个MapperRegistry.java中有个方法是addMappers();共有两个重载。
public void addMappers(String packageName, Class<?> superType)
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
//通过包名,查找该包下所有的接口进行遍历,放入集合中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet)
addMapper(mapperClass);


//解析包名下的接口
public void addMappers(String packageName)
addMappers(packageName, Object.class);

往上追溯该方法的调用是在SqlSessionFactory.build();时对配置文件的解析,其中对节点mappers的解析,这里先不赘述,

mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception 
if (parent != null) 
for (XNode child : parent.getChildren()) 
//使用package节点进行解析配置
if ("package".equals(child.getName())) 
String mapperPackage = child.getStringAttribute("name");
//注册包下的接口
configuration.addMappers(mapperPackage);
 else 
//使用mapper节点
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);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
 else if (resource == null && url != null && mapperClass == null) 
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
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.");





这是调用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<T>(type));
//根据接口名寻找同包下同名的xml或者mapper的namespace是该接口的xml
//找到对用的xml后进行解析mapper节点里面的节点
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
 finally 
if (!loadCompleted) 
knownMappers.remove(type);




这是通过接口的全路径来查找对应的xml。这里有两种方式解析,也就是我们平常xml文件放置位置的两种写法。
第一种是不加namespace,把xml文件放在和接口相同的路径下,同时xml的名字与接口名字相同,如接口名为
Student.java,xml文件为Student.xml。在相同的包下。这种当时可以不加namespace.
第二种是加namespace,通过namespace来查找对应的xml.
到这就是接口名和xml的全部注册流程。
下面再说下第二部分就是通过动态代理获取接口名字来对应xml中的id。
主要有两个类MapperProxyFactory.java和MapperProxy.java
对于MapperProxyFactory.java
public class MapperProxyFactory<T>
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
//构造函数,获取接口类

public MapperProxyFactory(Class<T> mapperInterface)
 
this.mapperInterface = mapperInterface;


public Class<T> getMapperInterface() 
return mapperInterface;


public Map<Method, MapperMethod> getMethodCache() 

return methodCache;


@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy)
 
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]
           
    mapperInterface , mapperProxy);


//供外部调用

public T newInstance(SqlSession sqlSession)
 
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface,             
    methodCache);
    return newInstance(mapperProxy);


在MapperProxy.java中进行方法的执行

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
if (Object.class.equals(method.getDeclaringClass())) 
try 
return method.invoke(this, args);
 catch (Throwable t) 
throw ExceptionUtil.unwrapThrowable(t);


final MapperMethod mapperMethod = cachedMapperMethod(method);
//方法的执行
return mapperMethod.execute(sqlSession, args);

private MapperMethod cachedMapperMethod(Method method) 
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) 
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);

return mapperMethod;


至此,就是mybatis所有接口和xml的加载,以及通过动态代理来进行接口的执行的过程。
总结
以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢
谢大家对我们的支持。

新人创作打卡挑战赛 发博客就能抽奖!定制产品红包拿不停!

以上是关于mybatis如何通过接口查找对应的mapper.xml及方法执行详解的主要内容,如果未能解决你的问题,请参考以下文章

mybatis 中一个接口可以对应多个mapper吗

mybatis中dao接口与mapper关联的理解

MyBatis的SQL映射文件

MyBatis之sql映射文件

mybatis无法扫描到对应的mapper,启动没问题,调接口就报错?

Mybatis中接口和对应的mapper文件位置配置详解