mybatis

一. 流程
在这里插入图片描述
二. SqlSessionFactoryBuilder中的build方法是如何创建出SqlsessionSactory

传入一个配置文件的文件输入流
public SqlSessionFactory build(InputStream inputStream) {
  return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    创建SqlSessionFactory 的过程重点在于XMLConfigBuilder的创建与parser.parse()的执行。new XMLConfigBuilder的过程解析出一个document对象放入XPathParser然后XPathParser会被放入XMLConfigBuilder对象。
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

XMLConfigBuilder对象的创建

调用此构造器
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    //重点在于这个赋值,下一步会用到这个属性
    this.parser = parser;
  }
XPathParser对象的创建,上一步构造方法需要这个对象的传入
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(inputStream));
  }
这是一个本类中的方法。给对象中的一些属性进行了赋值
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }
重点来看document这个对象的创建
private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
    这是调用抽象类的静态方法实例出DocumentBuilderFactory对象
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      得到这个DocumentBuilder调用这个parse创建一个document对象
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

以上是创建XMLConfigBuilder对象的过程。下面是执行
return build(parser.parse());的过程。parser.parse()执行完会生成一个configuration对象。执行完后会生成一个SqlSessionFactory对象。

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    这个方法便是对xml文件中内容的详细解析
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
以下下这些方法是创建SqlsessionFacory对象的关键。我们来一个一个进行阅读
private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      此标签 原来将一些被其他地方引用的变量存储在这。别的地方使用时用${}
      例如:
  

```xml
    <configuration>
    <properties>
        <property name="jdbc.username" value="test" /> 
        <property name="jdbc.password" value="test" /> 
    </properties>

    <environments default="development"> 
        <environment id="development"> 
            <transactionManager type="JDBC" /> 
            <dataSource type="POOLED">
                <property name="username" value="${jdbc.username}" /> 
                <property name="password" value="${jdbc.password}" /> 
        </dataSource> 
        </environment> 
    </environments>  
</configuration>
 propertiesElement(root.evalNode("properties"));
 用来存储一些全局的变量 <!-- 全局启用或禁用【延迟加载】。当禁用时,所有关联对象都会即时加载 -->
    <setting name="lazyLoadingEnabled" value="true" />


      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      别名映射
      typeAliasesElement(root.evalNode("typeAliases"));
      插件,如分页插件等
      pluginElement(root.evalNode("plugins"));
      当我们获取到结果集时,mybatis每次都会生成一个objectFactory,一般为默认
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      配置数据源 ,可配置多数据源
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      
      typeHandlerElement(root.evalNode("typeHandlers"));
      配置mapper接口所在位置。
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      如果事以这种方式配置,则接口和xml的名字要相同。
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
        src目录下
          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.");
          }
        }
      }
    }
  }
研究以下为什么配置package的方式必须要保证接口名字和xml的相同
packageName xml文件中配置的包名,superType Objectpublic void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    执行完这一步之后,接口会被全部加载进一个set集合
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    获取上一步加载的接口集合
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

new ResolverUtil.IsA(superType)
IsA是ResolverUtil类中的一个静态内部类。也是Test的一个子类。
实例化了一个将Object.class传进去的Test子类。

resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
看这里的find方法

public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
    遍历此包名路径下的内容,如果是接口进一步处理
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }
addIfMatching(test, child);

protected void addIfMatching(Test test, String fqn) {
    try {
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      if (log.isDebugEnabled()) {
        log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
      }
反射加载这个接口
      Class<?> type = loader.loadClass(externalName);
      这个类不为空且是实例话ISA的时候穿进去的那个类的一个子类
	      isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
	isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。
      if (test.matches(type)) {
      // 注意此处的类型 private Set<Class<? extends T>> matches = new HashSet<Class<? extends T>>();
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
      log.warn("Could not examine class '" + fqn + "'" + " due to a " +
          t.getClass().getName() + " with message: " + t.getMessage());
    }
  }
addMapper(mapperClass);

```java
public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
    判断这个MapperRegistry类中的集合Map<Class<?>, MapperProxyFactory<?>> knownMappers是否已经包含了这个接口
      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));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
    加载xml文件
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      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();
  }
private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
    注意此处xml文件的加载路径为类路径加.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();
      }
    }
  }
重点来看parseStatement(Method method)是对接口和xml文件的关联
这个方法是对注解上的sql进行解析

结束后将会把所有的sql信息存进assistant中


三. SqlsessionFactory创建Sqlsession的过程


四. 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值