Mybatis源碼分析:MapperRegistry


mapper注冊器(MapperRegistry)

   mapper注冊器用於將所有的mapper接口添加到內存中,Mapper注冊器自身維護着兩個屬性,config和knownMappers,其中knownMappers是一個 Class<?>, MapperProxyFactory<?>的集合,表示某個類路徑對應的Mapper代理工廠,MapperProxyFactory做的事比較簡單,目的就是通過代理模式創建處一個MapperProxy,MapperProxy實現了InvocationHandler接口,這表示MapperProxy會通過invoke()方法實現Mapper接口指定方法的調用,MapperProxy並不直接實現Mapper接口的調用,而是在內部維系着一個<Mapper.Method,MapperMethod>的map集合,在上節看到,MapperMethod中包裝了Sqlsession的相關方法,所以MapperProxy本質上只是代理了<Method,MapperMethod>之間的映射關系

MapperRegistry

MapperRegistry用於注冊,獲取和判斷是否Mapper接口已經被注冊的功能,主要提供了以下幾個方法

1.getMapper(Class type, SqlSession sqlSession) 從緩存集合中獲取對應的Mapper接口
2.hasMapper(Class type) 判斷緩存集合中是否存在Mapper接口
3.addMapper(Class type) 添加Mapper接口到knownMappers集合中
4.getMappers() 獲取knownMappers集合中所有已經注冊的Mapper接口,Mybatis3.2.2新增
5.addMappers(String packageName, Class<?> superType) 添加某個包下的所有符合superType的子類或者子接口 Mybatis3.2.2新增

  先看addMapper()方法,該方法的主要作用是將Mapper添加到knownMappers集合中,實現Mapper類到Mapper代理工廠的映射,代碼中有一處非常重要,將Mapper類添加至集合后還必須完成一次xml配置解析,如果解析不成功,那么仍然會將mapper接口移除,后續會講到MapperAnnotationBuilder是如何進行解析的。

 1 public <T> void addMapper(Class<T> type) {
 2       //判斷是否是接口
 3     if (type.isInterface()) {
 4         //判斷是否已經注冊過
 5       if (hasMapper(type)) {
 6         throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
 7       }
 8       boolean loadCompleted = false;
 9       try {
10           //將mapper接口注冊到map集合中
11         knownMappers.put(type, new MapperProxyFactory<T>(type));
12         // It's important that the type is added before the parser is run
13         // otherwise the binding may automatically be attempted by the
14         // mapper parser. If the type is already known, it won't try.
15         MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
16         parser.parse();
17         loadCompleted = true;
18       } finally {
19         if (!loadCompleted) {
20           knownMappers.remove(type);
21         }
22       }
23     }
24   }

getMapper(Class type, SqlSession sqlSession)

  該方法用於獲取已經注冊的Mapper,獲取方式直接從knownMappers集合中獲取,如果獲取不到,則使用Mapper代理工廠實例化一個MapperProxy,在開頭我們說過,這樣做的最終目的是為了獲取一個MapperMethod,也就是說,通過實例化代理工廠,取出mapper對應的MapperProxyFactory,然后以動態代理模式實例化ProxyMapper,其目的都是為了實現一個方法執行類,代理執行Mapper的某個方法。

 

 1 @SuppressWarnings("unchecked")
 2   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 3       this.notifyAll();
 4       //獲取mapper代理工廠
 5     final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
 6     //在map中找不到則表示沒有將mapper類注冊進來,拋出BindingException
 7     if (mapperProxyFactory == null) {
 8       throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 9     }
10     try {
11         //使用工廠進行創建一個實例,本質上是通過代理模式創建了一個代理類,創建過程中出現異常拋出BindingException
12       return mapperProxyFactory.newInstance(sqlSession);
13     } catch (Exception e) {
14       throw new BindingException("Error getting mapper instance. Cause: " + e, e);
15     }

 

MapperProxy

  mapperProxy應該算作一個包裝類,本身不做任何事情,它最主要的作用就是維系着<Method,MapperMethod>集合,這樣mapper接口就知道即將執行的是哪種SQL語句了,然后委托給Sqlsession進行查詢了。 看如下的代碼,該代碼的執行流程為:

1.判斷代理類是否是方法所在的類,如果是,直接執行該方法。
2.判斷是否是模式方法,如果是則執行默認方法,是否是默認方法的判斷條件如下

 

(method.getModifiers()
     & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
     && method.getDeclaringClass().isInterface()

3.如果以上兩者都不是,那么從緩存的集合中取出對應的MapperMethod,調用其中的execute方法。

  這里應該注意的是,invokeDefaultMethod()方法中使用了@UsesJava7注解,表明最低版本應當使用JDk7,原因是因為使用了MethodHandlers,這個類后續會進行講解,現在只需要知道它跟反射作用相同並且能夠進行方法的調用。

 1 @Override
 2   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 3     try {
 4         //判斷代理類是否是Method所在的類
 5       if (Object.class.equals(method.getDeclaringClass())) {
 6           //執行該方法
 7         return method.invoke(this, args);
 8       }//判斷是否是默認的方法
 9       else if (isDefaultMethod(method)) {
10           //調用默認方法
11         return invokeDefaultMethod(proxy, method, args);
12       }
13     } catch (Throwable t) {
14       throw ExceptionUtil.unwrapThrowable(t);
15     }
16     //從緩存中取出方法對應的mapperMethod
17     final MapperMethod mapperMethod = cachedMapperMethod(method);
18     //執行execute()方法
19     return mapperMethod.execute(sqlSession, args);
20   }
 1  @UsesJava7
 2   private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
 3       throws Throwable {
 4     final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
 5         .getDeclaredConstructor(Class.class, int.class);
 6     if (!constructor.isAccessible()) {
 7       constructor.setAccessible(true);
 8     }
 9     final Class<?> declaringClass = method.getDeclaringClass();
10     return constructor
11         .newInstance(declaringClass,
12             MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
13                 | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
14         .unreflectSpecial(method, declaringClass)
15         .bindTo(proxy)
16         .invokeWithArguments(args);
17   }

 

關於MapperRegistry的總結

  MapperRegistry本質上用於注冊Mapper接口和獲取MapperProxy類,MapperProxy並通過MapperProxyFactory進行實例化,然而在MapperProxy的invoke()方法中會調用Mapper接口與之對應的MapperMethod類中的execute()方法。MapperRegistry依賴於Mapper類路徑,SqlSession作為入參,結合MapperProxy,MapperProxyFactory,ResolverUtil,MapperAnnotationBuilder等組件才完成了一次注冊Mapper到sql語句的執行之旅。

下圖為MapperRegistry所關聯的類

 


免責聲明!

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



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