mybatisはどのようにインタフェースを通じて対応するmapper.xmlを検索しますか?


ここでは主にmybatisについて、インタフェースを通じて、mapper.xml及び方法の実行に対応する内容を検索します。
mybatisを使う時、一つの方法があります。

BookMapper bookMapper = SqlSession().getMapper(BookMapper.class)
インターフェースを取得してインターフェースを呼び出す方法です。メソッド名と対応するmapper.xmlのidの名前が同じであれば、sqlを実行できます。
インターフェースはどのようにmapper.xmlに対応しますか?
まず、getMapper()で方法はどうやって操作されますか?
Default Sql Session.Javaでconfiguration.getMapper()を呼び出しました。

public <T> T getMapper(Class<T> type) {
 return configuration.<T>getMapper(type, this);
 }
Configration.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();に2つの重さがある方法があります。

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をインタフェースの全パスで検索することです。ここには2つの方法の解析があります。つまり、私たちは普段xmlファイルを置く場所の2つの書き方です。
一つは、インターフェース名Student.java、xmlファイルはStudent.xmlと同じ経路で、namespaceを入れないことです。同じカバンの下で。このような当時はnamespaceを入れなくてもいいです。
二つ目はカナメスペースで、namespaceを通して対応するxmlを探します。
これまではインターフェース名とxmlの全部の登録の流れです。
次に第二部分を説明します。動的エージェントを通じてインターフェースの名前を取得して、xml中のidに対応します。
主に二つの種類のMapper ProxyFactory.javaとMapperProxy.javaがあります。
Mapper ProxyFactory.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);
 }

}
Mapper Proxy.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のローディング、および動的エージェントによるインターフェースの実行プロセスです。
締め括りをつける
以上はこの文章の全部の内容です。本文の内容は皆さんの学習や仕事に一定の助けをもたらすことを願っています。もし疑問があれば、メッセージを残して交流してください。ありがとうございます。