三、创建Mapper对象原理

上节二、SqlSessionFactoryBean和MapperFactoryBean作用我们分析MapperFactoryBean对象在初始化的时候会将Mapper接口添加到Configuration对象中,在getObject时又会从Configuration对象中获取一个Mapper对象。这背后到底发生了什么,传入一个接口是如何返回一个对象的,是我们这节分析的重点。

Configuration#addMapper(Class type)

 public  void addMapper(Class type) {
    mapperRegistry.addMapper(type);
  }

把工作交给了mapperRegistry去处理。

public  void addMapper(Class type) {
    //1. 验证传入的类型是不是接口
    if (type.isInterface()) {
      //2. 验证是否已经加载过
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //3. 添加mapper接口到knownMappers,表示已经注册
        knownMappers.put(type, new MapperProxyFactory(type));
        //4. 解析Mapper对应的xml
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
 }

addMapper的逻辑如下:

  1. 验证传入的类型是不是接口
  2. 验证是否已经加载过
public  boolean hasMapper(Class type) {
    return knownMappers.containsKey(type);
}
  1. 添加mapper接口到knownMappers,创建MapperProxyFactory对象,这个对象的作用在下面会讲。
  2. 解析Mapper对应的xml文件,详情看下一小节。

MapperAnnotationBuilder

MapperAnnotationBuilder作用是用来解析Mapper接口的xml文件和Mapper接口上的注解。看下它的parse方法。

public void parse() {
    String resource = type.toString();
    //1. 判断类是否加载过
    if (!configuration.isResourceLoaded(resource)) {
      //2. 加载对应的xml文件
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      //3. 后面的都是解析接口中的注解
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
}

解析的过程如下:

  1. 判断类是否加载过
  2. 解析对应的xml文件,根据接口全名获取xml路径
private void loadXmlResource() {
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
    //根据接口全名获取xml路径
    String xmlResource = type.getName().replace('.', '/') + ".xml";
    InputStream inputStream = null;
    try {
    inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
    } catch (IOException e) {
    // ignore, resource is not required
    }
    if (inputStream != null) {
    XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
    xmlParser.parse();
    }
}
}
  1. 解析类中注解

Configuration#getMapper(Class type, SqlSession sqlSession)

public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

getMapper方法还是交给了mapperRegistry处理。

public  T getMapper(Class type, SqlSession sqlSession) {
    //1. 获得接口对应的MapperProxyFactory对象,这个对象是在addMapper中创建的
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //2. MapperProxyFactory通过动态代理创建对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}
  1. 获得接口对应的MapperProxyFactory对象,这个对象是在addMapper中创建的
  2. MapperProxyFactory通过动态代理根据接口创建对象, 看下MapperProxyFactory对象的newInstance方法
public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

动态代理创建对象的业务逻辑交给了MapperProxy对象。

总结

MapperFactoryBean传入一个接口,是把接口注册到Configuration对象中,并且解析对应的xml。MapperFactoryBean获取的对象是根据传入的接口动态代理创建了一个对象,对象的逻辑交给了MapperProxy处理。

你可能感兴趣的:(三、创建Mapper对象原理)