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所关联的类