MyBatis启动:SqlSessionFactory的建立过程

来源:互联网 发布:mysql 导入数据乱码 编辑:程序博客网 时间:2024/05/22 08:39

MyBatis的启动,就是建立SqlSessionFactory的过程,读取配置信息,通过会话工程建造者创建会话工厂。SqlSessionFactory接口定义了从数据源、链接中打开会话的方法以及获取配置信息的方法。以DefaultSqlSessionFactory为例,创建过程就是创建Configuration对象,赋值给工程对象。

public SqlSessionFactory build(Configuration config) {    return new DefaultSqlSessionFactory(config);  }
创建Configuration对象的过程,是解析XML文件的过程,并且在过程中创建对象所依赖的对象。

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());

首先创建XMLConfigBuidler对象用来解析XML配置文件工作,这个对象中持有XPathParser对象,这个才是真正用来解析XML的工具类。底层采用JDK实现的XPath的API来对XML进行解析工作,此处不深入说明。重点关注Configuration对象的创建过程。

public Configuration parse() {    if (parsed) {      throw new BuilderException("Each XMLConfigBuilder can only be used once.");    }    parsed = true;    parseConfiguration(parser.evalNode("/configuration"));    return configuration;  }
这个XML创建者将XML文档创建结束后,调用parse()方法进行解析,方法中调用parseConfiguration()方法对configuration节点进行解析。parser.evalNode其实就是解析XML文件返回代表configuration节点的XNode对象,然后再对XNode对象进行粒度更细的解析。
  private void parseConfiguration(XNode root) {    try {      propertiesElement(root.evalNode("properties")); //issue #117 read properties first      typeAliasesElement(root.evalNode("typeAliases"));      pluginElement(root.evalNode("plugins"));      objectFactoryElement(root.evalNode("objectFactory"));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      settingsElement(root.evalNode("settings"));      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631      databaseIdProviderElement(root.evalNode("databaseIdProvider"));      typeHandlerElement(root.evalNode("typeHandlers"));      mapperElement(root.evalNode("mappers"));    } catch (Exception e) {      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);    }  }

解析configuration节点下的所有可能配置的子节点。首先是Properties,可以从文件中读取,也可以通过URL从网络中获取,还有就是从配置文件中获取。获取到的Properties对象将注入到configuration对象中。

private void propertiesElement(XNode context) throws Exception {    if (context != null) {      Properties defaults = context.getChildrenAsProperties();      String resource = context.getStringAttribute("resource");      String url = context.getStringAttribute("url");      if (resource != null && url != null) {        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");      }      if (resource != null) {        defaults.putAll(Resources.getResourceAsProperties(resource));      } else if (url != null) {        defaults.putAll(Resources.getUrlAsProperties(url));      }      Properties vars = configuration.getVariables();      if (vars != null) {        defaults.putAll(vars);      }      parser.setVariables(defaults);      configuration.setVariables(defaults);    }  }
接下来解析类型的别名,可以配置包名,MyBatis会通过反射获取包下的所有类型,并且通过typeAliasRegistry注册类型的别名。这个TypeAliasRegistry内部持有一个Map,key用来保存别名alias,value用来保存类信息。比如:"byte", Byte.class。
private void typeAliasesElement(XNode parent) {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String typeAliasPackage = child.getStringAttribute("name");          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);        } else {          String alias = child.getStringAttribute("alias");          String type = child.getStringAttribute("type");          try {            Class<?> clazz = Resources.classForName(type);            if (alias == null) {              typeAliasRegistry.registerAlias(clazz);            } else {              typeAliasRegistry.registerAlias(alias, clazz);            }          } catch (ClassNotFoundException e) {            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);          }        }      }    }  }
解析插件plugins。

  private void pluginElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();        interceptorInstance.setProperties(properties);        configuration.addInterceptor(interceptorInstance);      }    }  }
  public <T> Class<T> resolveAlias(String string) {    try {      if (string == null) return null;      String key = string.toLowerCase(Locale.ENGLISH); // issue #748      Class<T> value;      if (TYPE_ALIASES.containsKey(key)) {        value = (Class<T>) TYPE_ALIASES.get(key);      } else {        value = (Class<T>) Resources.classForName(string);      }      return value;    } catch (ClassNotFoundException e) {      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);    }  }
解析配置拦截器的名称,首先别名注册器上寻找,找到直接返回类信息Class对象,找不到则通过类加载器去编译路径找寻找对应的类信息。找到后调用newInstance得到实例对象。然后将拦截器添加到configuration对象的拦截器链上。

  private void objectFactoryElement(XNode context) throws Exception {    if (context != null) {      String type = context.getStringAttribute("type");      Properties properties = context.getChildrenAsProperties();      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();      factory.setProperties(properties);      configuration.setObjectFactory(factory);    }  }
解析对象工厂,并设置到configuration对象,可以覆盖原来对象工厂的行为。接下来是解析objectWrapperFactory节点,和解析工厂类似。

解析setting节点,这个将会改变configuration对象上的很多默认熟悉,也会改变mybatis的行为。

private void settingsElement(XNode context) throws Exception {    if (context != null) {      Properties props = context.getChildrenAsProperties();      // Check that all settings are known to the configuration class      MetaClass metaConfig = MetaClass.forClass(Configuration.class);      for (Object key : props.keySet()) {        if (!metaConfig.hasSetter(String.valueOf(key))) {          throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");        }      }      configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));      configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));      configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));      configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));      configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));      configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));      configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));      configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));      configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));      configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));      configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));      configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));      configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));      configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));      configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));      configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));      configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));      configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));      configuration.setLogPrefix(props.getProperty("logPrefix"));      configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));      configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));      configuration.setInjectionFilterEnabled(booleanValueOf(props.getProperty("injectionFilterEnabled"), false));      configuration.setInjectionFilter(parseExpression(props.getProperty("injectionFilter"), "^[a-zA-Z0-9._]*$"));    }  }
接着解析environments这个比较重要,不仅是新建environments对象设置到configuration中,而且还将创建事务工厂对象,以及数据源对象。
private void environmentsElement(XNode context) throws Exception {    if (context != null) {      if (environment == null) {        environment = context.getStringAttribute("default");      }      for (XNode child : context.getChildren()) {        String id = child.getStringAttribute("id");        if (isSpecifiedEnvironment(id)) {          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));          DataSource dataSource = dsFactory.getDataSource();          Environment.Builder environmentBuilder = new Environment.Builder(id)              .transactionFactory(txFactory)              .dataSource(dataSource);          configuration.setEnvironment(environmentBuilder.build());        }      }    }  }
接下来解析databaseIdProvider,通过配置数据库供应商节点,可以在statement语句上标识对应数据库的语句,执行时将根据不同的数据库执行对应的语句。

解析typeHandlers,将处理Java类型和数据库类型的转换关系。解析TyptHandler的同样通过一个注册器来管理信息。

 private void typeHandlerElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        if ("package".equals(child.getName())) {          String typeHandlerPackage = child.getStringAttribute("name");          typeHandlerRegistry.register(typeHandlerPackage);        } else {          String javaTypeName = child.getStringAttribute("javaType");          String jdbcTypeName = child.getStringAttribute("jdbcType");          String handlerTypeName = child.getStringAttribute("handler");          Class<?> javaTypeClass = resolveClass(javaTypeName);          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);          Class<?> typeHandlerClass = resolveClass(handlerTypeName);          if (javaTypeClass != null) {            if (jdbcType == null) {              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);            } else {              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);            }          } else {            typeHandlerRegistry.register(typeHandlerClass);          }        }      }    }  }

解析mappers。

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

同样可以通过类路径查找,网络资源查找mapper的信息,然后使用mapper注册器注册。看一下最后一种方式,Class<?> mapperInterface = Resources.classForName(mapperClass);先加载类文件信息,然后调用configuration.addMapper(mapperInterface);来注册。而configuration中mapperRegistry.addMapper(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 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);        }      }    }  }
mapper的注册也是放到一个map中,key是类型名称,value是类型的代理工厂。然后解析mapper的xml文件。关于mapper的创建过程,将在下一篇中分析。





0 1