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所關聯的類