MyBatis启动:SqlSessionFactory的建立过程

本文详细解析了MyBatis框架的初始化过程,包括SqlSessionFactory的创建、Configuration对象的构建及各个核心组件的装配机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/xushucan/article/details/38986283


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

[java]  view plain  copy
  1. public SqlSessionFactory build(Configuration config) {  
  2.     return new DefaultSqlSessionFactory(config);  
  3.   }  
创建Configuration对象的过程,是解析XML文件的过程,并且在过程中创建对象所依赖的对象。

[java]  view plain  copy
  1. XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);  
  2. return build(parser.parse());  

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

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

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

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

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

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

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

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

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

[java]  view plain  copy
  1. private void typeHandlerElement(XNode parent) throws Exception {  
  2.    if (parent != null) {  
  3.      for (XNode child : parent.getChildren()) {  
  4.        if ("package".equals(child.getName())) {  
  5.          String typeHandlerPackage = child.getStringAttribute("name");  
  6.          typeHandlerRegistry.register(typeHandlerPackage);  
  7.        } else {  
  8.          String javaTypeName = child.getStringAttribute("javaType");  
  9.          String jdbcTypeName = child.getStringAttribute("jdbcType");  
  10.          String handlerTypeName = child.getStringAttribute("handler");  
  11.          Class<?> javaTypeClass = resolveClass(javaTypeName);  
  12.          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);  
  13.          Class<?> typeHandlerClass = resolveClass(handlerTypeName);  
  14.          if (javaTypeClass != null) {  
  15.            if (jdbcType == null) {  
  16.              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);  
  17.            } else {  
  18.              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);  
  19.            }  
  20.          } else {  
  21.            typeHandlerRegistry.register(typeHandlerClass);  
  22.          }  
  23.        }  
  24.      }  
  25.    }  
  26.  }  

解析mappers。

[java]  view plain  copy
  1. private void mapperElement(XNode parent) throws Exception {  
  2.     if (parent != null) {  
  3.       for (XNode child : parent.getChildren()) {  
  4.         if ("package".equals(child.getName())) {  
  5.           String mapperPackage = child.getStringAttribute("name");  
  6.           configuration.addMappers(mapperPackage);  
  7.         } else {  
  8.           String resource = child.getStringAttribute("resource");  
  9.           String url = child.getStringAttribute("url");  
  10.           String mapperClass = child.getStringAttribute("class");  
  11.           if (resource != null && url == null && mapperClass == null) {  
  12.             ErrorContext.instance().resource(resource);  
  13.             InputStream inputStream = Resources.getResourceAsStream(resource);  
  14.             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());  
  15.             mapperParser.parse();  
  16.           } else if (resource == null && url != null && mapperClass == null) {  
  17.             ErrorContext.instance().resource(url);  
  18.             InputStream inputStream = Resources.getUrlAsStream(url);  
  19.             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());  
  20.             mapperParser.parse();  
  21.           } else if (resource == null && url == null && mapperClass != null) {  
  22.             Class<?> mapperInterface = Resources.classForName(mapperClass);  
  23.             configuration.addMapper(mapperInterface);  
  24.           } else {  
  25.             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");  
  26.           }  
  27.         }  
  28.       }  
  29.     }  
  30.   }  

同样可以通过类路径查找,网络资源查找mapper的信息,然后使用mapper注册器注册。看一下最后一种方式,Class<?> mapperInterface = Resources.classForName(mapperClass);先加载类文件信息,然后调用configuration.addMapper(mapperInterface);来注册。而configuration中mapperRegistry.addMapper(type);通过注册器来注册。

[java]  view plain  copy
  1. public <T> void addMapper(Class<T> type) {  
  2.   if (type.isInterface()) {  
  3.     if (hasMapper(type)) {  
  4.       throw new BindingException("Type " + type + " is already known to the MapperRegistry.");  
  5.     }  
  6.     boolean loadCompleted = false;  
  7.     try {  
  8.       knownMappers.put(type, new MapperProxyFactory<T>(type));  
  9.       // It's important that the type is added before the parser is run  
  10.       // otherwise the binding may automatically be attempted by the  
  11.       // mapper parser. If the type is already known, it won't try.  
  12.       MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);  
  13.       parser.parse();  
  14.       loadCompleted = true;  
  15.     } finally {  
  16.       if (!loadCompleted) {  
  17.         knownMappers.remove(type);  
  18.       }  
  19.     }  
  20.   }  
  21. }  
mapper的注册也是放到一个map中,key是类型名称,value是类型的代理工厂。然后解析mapper的xml文件。关于mapper的创建过程,将在下一篇中分析。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值