标签:uil 文件 contain 过程 dex default 第一部分 class void
转:http://www.jb51.net/article/116402.htm
本文主要介绍的是关于mybatis通过接口查找对应mapper.xml及方法执行的相关内容,下面话不多说,来看看详细的介绍:
在使用mybatis的时候,有一种方式是
| 
 1 
 | 
BookMapper bookMapper = SqlSession().getMapper(BookMapper.class) | 
获取接口,然后调用接口的方法。只要方法名和对应的mapper.xml中的id名字相同,就可以执行sql。
那么接口是如何与mapper.xml对应的呢?
首先看下,在getMapper()方法是如何操作的。
在DefaultSqlSession.Java中调用了configuration.getMapper()
| 
 1 
2 
3 
 | 
public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } | 
在Configuration.java中调用了mapperRegistry.getMapper(type, sqlSession);
| 
 1 
2 
3 
 | 
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } | 
下面重点来了,在MapperRegistry.java中实现了动态代理
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
 | 
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();共有两个重载。
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
 | 
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的解析,这里先不赘述,
| 
 1 
 | 
mapperElement(root.evalNode("mappers")); | 
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
 | 
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()的顺序。
同时在改方法中还有一个方法很重要
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
 | 
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
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
 | 
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中进行方法的执行
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
 | 
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及方法执行详解
标签:uil 文件 contain 过程 dex default 第一部分 class void
原文地址:https://www.cnblogs.com/wangle1001986/p/9072630.html