zoukankan      html  css  js  c++  java
  • 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所关联的类

  • 相关阅读:
    快速排序算法C++实现[评注版]
    浮躁的程序员
    扬长避短使用Windbg和Visual Studio高效调试调试你的代码
    程序员,代码,理想,老男孩
    Windows Server 2008 R2 如何启动kernel dbg进行双机内核调试『续bcdedit 用法详解』
    Windows Server 2008 R2 如何启动kernel dbg进行双机内核调试『配置详解』
    忙着活或忙着死[转]
    SQL2005使用游标的实例(SBO中计算到期应收账款)
    C#编写的Windows计算器源代码
    请登录真正的BBS
  • 原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11234690.html
Copyright © 2011-2022 走看看