Mybatis源码学习(16)-binding模块之MapperProxy、MapperRegistry类

导读:本篇文章讲解 Mybatis源码学习(16)-binding模块之MapperProxy、MapperRegistry类,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、前言

  在上一篇《Mybatis源码学习(15)-binding模块之MapperMethod类》中,已经概述了Mybatis中binding模块的相关概念,同时详细分析了MapperMethod类。这一篇我们来分析binding模块中剩余的类,分别是MapperProxyFactory、MapperProxy、MapperRegistry等。

二、MapperProxy类

  MapperProxy类是InvocationHandler接口的实现,用来在面向接口编程过程中,根据接口、对应的XML文件产生的一个代理实现的代理对象。

  • 字段、构造函数
    在MapperProxy类中,有三个关键属性字段,其中,sqlSession用来记录关联的SqlSession对象,其实再生成的代理对象实例中,真正操作数据库SQL语句的时候,还是由SqlSession对象完成的;mapperInterface字段对应了该代理对象对应的Mapper接口;methodCache字段用来缓存MapperMethod对象,MapperMethod对象定义了Mapper接口中对应方法的信息以及对应SQL语句的信息,用来完成参数转换以及SQL语句的执行功能。构造函数主要是用来初始化实例对象,同时为这三个字段赋值。
  //记录关联的SqlSession对象
  private final SqlSession sqlSession;
  //记录Mapper接口对应的Class对象
  private final Class<T> mapperInterface;
  //用于缓存MapperMethod对象,其中key是Mapper接口中方法对应的Method对象, value是对应的MapperMethod对象。
  //MapperMethod对象会完成参数转换以及SQL语句的执行功能
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }
  • invoke()方法
    动态代理对象的核心处理逻辑,首先通过cachedMapperMethod()方法获取方法对应的MapperMethod对象,获取过程中如果没有在缓存中,就加入到缓存中,然后根据MapperMethod对象在进行真正的数据库的相关操作,即在MapperMethod.execute()方法中真正实现数据库操作逻辑。MapperMethod.execute()方法的具体实现在上一篇已经分析,这里不再重复。
@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {//如果目标方法继承自Object,则直接调用目标方法
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {//针对Java7以上版本对动态类型语言的支持,不进行详述
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
三、MapperProxyFactory类

  MapperProxyFactory类主要用来创建代理对象,比较简单,代码如下所示:

public class MapperProxyFactory<T> {
  //当前MapperProxyFactory对象可以创建实现了mapperInterface接口的代理对象
  private final Class<T> mapperInterface;
  //缓存, key是mapperinterface接口中某方法对应的Method对象, value是对应的MapperMethod对象
  private final 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;
  }
  /**
   * 创建实现了mapperinterface接口的代理对象
   * @param mapperProxy
   * @return
   */
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
  /**
   * 创建MapperProxy对象,每次调用都会创建新的MapperProxy对象
   * @param sqlSession
   * @return
   */
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

三、MapperRegistry类

  MapperRegistry类是Mapper接口及其对应的代理对象工厂的注册中心。

  • 字段、构造函数
    其中,Configuration对象, MyBatis全局唯一的配置对象,其中包含了所有配置信息;knownMappers 记录了Mapper接口与对应MapperProxyFactory之间的关系。构造函数还是用来实例化对象和字段赋值。
  //Configuration对象, MyBatis全局唯一的配置对象,其中包含了所有配置信息
  private final Configuration config;
  //记录了Mapper接口与对应MapperProxyFactory之间的关系
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }
  • getMapper()方法
    根据对应的类获取对应的代理对象。供Configuration对象中的getMapper()方法使用。
@SuppressWarnings("unchecked")
  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);
    }
  }
  • addMapper()方法
    相关方法主要用来在加载和解析配置文件的时候,初始化knownMappers字段。后期执行SQL方法的时候,再根据对应的类进行创建对应的代理对象。
/**
   * 在MyBatis初始化过程中会读取映射配置文件以及Mapper接口中的注解信息,
   * 并调用MapperRegisty.addMapper()方法填充M叩perRegistry.knownMappers集合, 
   * 该集合的key是Mapper接口对应的Class对象, value为MapperProxyFactory工厂对象。
   * 
   * @param type
   */
  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值,默认为false,标识是否加载完成
      boolean loadCompleted = false;
      try {
    	//将Mapper接口对应的Class对象和MapperProxyFactory对象添加到knownMappers集合
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        //对使用注解方式的实现进行注册(一般不使用)
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        //设置loadCompleted的值为true,表示注册完成
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {//对注册失败的类型进行清除
          knownMappers.remove(type);
        }
      }
    }
  }

/**
   * 将包下满足以superType为超类的Mapper接口及其对应的代理对象工厂注册到注册中心中
   * @since 3.2.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);
    }
  }
/**
   * 用于仅指定包名的情况下,扫描包下的每个映射器进行注册
   * @since 3.2.2
   */
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68896.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!