mybatis如何通過接口查找對應的mapper.xml及方法執行詳解


轉: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的加載,以及通過動態代理來進行接口的執行的過程。

總結

以上就是這篇文章的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM