对于mybatis执行Sql语句的探寻

.....................................................未完结,个人笔记,查资料、解惑者勿看...........................................................................

由于想弄懂一些事情,所以不得不翻出源码看看,又估计会比较麻烦,所以边找边记录下来。

先贴一段mybatis查询的一段代码,如下:

RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
roleMapper.findRoles("role");

事实上能看出来用到了动态代理技术,但是并没有具体的实现类,mybatis是怎么让代理类对象执行相关的SQL呢?

这个是我想要知道的东西。

上面sqlSessionDefaultSqlSession类的实例,即sqlSession.getMapper(RoleMapper.class)调用的是DefaultSqlSession类中定义的方法,如下:

  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

传入的是RoleMapper.class,接下来调用了Configuration类的getMapper(type, this)方法,定义如下:

configuration.getMapper(RoleMapper.class, sqlSession);
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

调用了MapperRegistry类的getMapper方法,定义如下:

  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);
    }
  }

终于看到不太一样的方法了,在该方法里首先通过get(type)获取MapperProxyFactory实例,并判断其是否为空,

为空,则抛出异常;不为空,调用其newInstance(sqlSession)方法作为返回值。(这应该是判断type有没有被注册)

到了这里,我们陆续传进来的两个参数都被使用了。首先看看MapperProxyFactory实例是如何产生的。

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = 
new HashMap<Class<?>, MapperProxyFactory<?>>();

有点头绪了吧,knownMappers是一个map,key是诸如RoleMapper.class的类对象,value是MapperProxyFactory实例。

很显然,这是在mybatis初始化时做的操作,具体的不谈,直接找出MapperProxyFactory存的是什么东东。

  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

上面传入的parent是<mappers>标签,child是一个个<mapper>或者<package>,如此很清楚了。

1.首先判断是<mapper>还是<package>(只讨论mapper);

2.三个getStringAttribute分别是三种注册mapper的方式;

3.接下来一长串的if语句,说明了三种注册mapper的方式只能存在一个;

我使用resource引入的,故只分析下面的一段代码。(resource是RoleMapper.xml文件的全路径名)

if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);//加载Mapper的resource文件
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } 

到了这里,首先将保存SQL语句的xml文件作为输入流new了一个XMLMapperBuilder对象,然后执行其parse方法。

  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

1.如果configuration没有加载文件,则解析RoleMapper.xml文件,configurationElement(parser.evalNode("/mapper")),

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

看到了什么?这不是对xml文件的配置吗?namespace, cache, cache-ref, parameterMap, resultMap, sql

及select|insert|update|delete子节点全都有对应操作,忍不住追溯源代码...

真是相当麻烦的,好了,这部分先过去,还是先看看MapperProxyFactory存的是什么东东

2.然后资源添加到configuration的Set<class.name>中,configuration.addLoadedResource(resource);

3.绑定Mapper的命名空间 ,bindMapperForNamespace();

  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          configuration.addLoadedResource("namespace:" + namespace);
          configuration.addMapper(boundType);
        }
      }
    }
  }

直接看addMapper方法,发现其调用了MapperRegistry类的该方法

  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 loadCompleted = false;
      try {
        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;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

终于看到knownMappers.put(type, new MapperProxyFactory<T>(type));

ε=(´ο`*)))唉,忙活半天结果它是new出来的

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

回过头来

  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);
    }
  }

这里得到的mapperProxyFactory的mapperInterface属性值是RoleMapper.class

调用newInstance(sqlSession)

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

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

到这里看到动态代理了,至于返回的代理对象如何能执行SQL还没弄清楚,

但是可以猜测,上面的methodCache,以及SQL语句的配置还没具体看,可能是和这两个部分有关,

额,以后有时间再补充o(╯□╰)o.............................

 

猜你喜欢

转载自blog.csdn.net/CJ95YD/article/details/82220238