Mybatis3源码分析(02)-加载Configuration-XMLConfigBuilder
2015-12-17 10:54
483 查看
Configuration类在Mybatis中的作用
Configuration类保存了所有Mybatis的配置信息。也就是说mybaits-config.xml及UserMapper.xml中所有配置信息都可以在Configruation对象中找到相应的信息。一般情况下Mybatis在运行过程中只会创建一个Configration对象,并且配置信息不能再被修改。如何配置Mybatis可以看这个文档:http://mybatis.org/mybatis-3/zh/configuration.htmlConfiguration的属性
configuration的属性主要分为两大部分:从mybatis-config.xml中读取的配置
从mapper配置文件或Mapper注解读取的配置
下面简单说一下这两部分的属性与配置的对应关系
从mybatis-config.xml文件中对应的属性
protected boolean safeRowBoundsEnabled = false; protected boolean safeResultHandlerEnabled = true; protected boolean mapUnderscoreToCamelCase = false; protected boolean aggressiveLazyLoading = true; protected boolean multipleResultSetsEnabled = true; protected boolean useGeneratedKeys = false; protected boolean useColumnLabel = true; protected boolean cacheEnabled = true; protected boolean callSettersOnNulls = false; protected String logPrefix; protected Class <? extends Log> logImpl; protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION; protected JdbcType jdbcTypeForNull = JdbcType.OTHER; protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" })); protected Integer defaultStatementTimeout; protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; protected Properties variables = new Properties(); protected ObjectFactory objectFactory = new DefaultObjectFactory(); protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory(); protected MapperRegistry mapperRegistry = new MapperRegistry(this); protected boolean lazyLoadingEnabled = false; protected ProxyFactory proxyFactory; protected final InterceptorChain interceptorChain = new InterceptorChain(); protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(); protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();以上属性可以说都是由mybatis-config.xml文件中读取的。
例如文件中的setting配置
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
看配置的内容和Configuration中的属性名称,就大概知道对应关系。相信之后的解析内容了不会太复杂。
从Mapper配置文件中读取的属性
如下属性是从Mapper配置文件中读取的protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection"); protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection"); protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection"); protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection"); protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");其中最主要的也是相对复杂的有如下两个(Mapper配置文件也主要是配置这两项):
mappedStatements属性,保存了所有Mapper配置文件中的select/update/insert/delete节点信息。属性类型为一个Map,key为sql对应的ID,MappedSatement为一个java对象,保存了一个select/update/insert/delete的节点信息。
resultMaps属性,保存了所有Mapper配置文件中的resultMap节点。
Mapper配置文件也主要是配置select/update/insert/delete/resultMap这几个节点。
Configuration加载过程
针对mybatis-config.xml配置文件和Mapper配置文件,Mybatis也是由两个相对应的类来解析的。XMLConfigBuilder解析mybatis-config.xml的配置到Configuration中
XMLMapperBuilder解析Mapper配置文件的配置到Configuration中
XMLConfigBuilder.parse()方法
通过SqlSessionFactory获取Configuration的代码SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is); System.out.println(sqlSessionFactory.getConfiguration());
再来看SqlSessionFactoryBuilder.build()方法:
public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //从这里可以看出XMLConfigBuilder.parse()返回了一个Configuration对象 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. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
加载具体配置
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } //从xml配置文件中加载到Configuration对象中 private void parseConfiguration(XNode root) { try { //加载properties节点,一般是定义一些变量 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")); //加载Mapper的配置文件,最主要的有两个:一个是sql的定义,一个是resultMap typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
加载properties节点
private void propertiesElement(XNode context) throws Exception { if (context != null) { //先加载property子节点下的属性 Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); //不能同时设置resource属性和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 sp b7fe ecify 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); } }
从这个方法中可以看出配置规则
可以设置url或resource属性从外部文件中加载一个properties文件
可以通过property子节点进行配置,如果子节点属性的key与外部文件的key重复的话,子节点的将被覆
通过编程方式定义的属性最后加载,优先级最高:
public SqlSessionFactory build(InputStream inputStream, Properties properties)
properties配置示例
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>
这里加载的主要是给后面的配置作为变量使用!
加载别名
private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { //package的方式,很少用到,略过 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); } } } } }
再看TypeAliasRegistry源码,发现mybatis已经为定义了很多别名,方便以后的配置
public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class); }
还有一个加载通过别名加载class的方法
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); } }
加载Mapper配置文件
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对象解析加载 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对象解析加载 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的配置文件最终会由XMLMapperBuilder对象解析加载到Configuration对象中。XMLMapperBuilder的解析过程中XMLConfigBuilder解析过程差不多,以后再详细分析!
加载其他配置项
还有一些配置项这里没有讲到,如:插件/拦截器、对象工厂、setting项。这些的加载都比较简单,只要花点心里就可以看明白。在以后分析代码过程中,一定会看到这里配置,到时再进一步研究,不过可以肯定这里配置很多情况下都是使用默认的值。相关文章推荐
- jdbc中的Statement和PreparedStatement接口对象
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- SQL中的三值逻辑
- 插入排序
- 冒泡排序