文章內索引
- Configuration類在Mybatis中的作用
- Configuration的屬性
- 從mybatis-config.xml文件中對應的屬性
- 從Mapper配置文件中讀取的屬性
- Configuration加載過程
- XMLConfigBuilder.parse()方法
- 加載properties節點
- 加載別名
- 加載Mapper配置文件
- 加載其他配置項
Configuration類在Mybatis中的作用
Configuration類保存了所有Mybatis的配置信息。也就是說mybaits-config.xml及UserMapper.xml中所有配置信息都可以在Configruation對象中找到相應的信息。一般情況下Mybatis在運行過程中只會創建一個Configration對象,並且配置信息不能再被修改。如何配置Mybatis可以看這個文檔:http://mybatis.org/mybatis-3/zh/configuration.html
Configuration的屬性
configuration的屬性主要分為兩大部分:
- 從mybatis-config.xml中讀取的配置
- 從mapper配置文件或Mapper註解讀取的配置
下面簡單說一下這兩部分的屬性與配置的對應關係
從mybatis-config.xml文件中對應的屬性
<code> 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();/<string>/<string>/<code>
以上屬性可以說都是由mybatis-config.xml文件中讀取的。
例如文件中的setting配置
<code> <settings> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> <setting> /<settings>/<code>
看配置的內容和Configuration中的屬性名稱,就大概知道對應關係。相信之後的解析內容了不會太複雜。
從Mapper配置文件中讀取的屬性
如下屬性是從Mapper配置文件中讀取的
<code> protected final Map<string> mappedStatements = new StrictMap<mappedstatement>("Mapped Statements collection"); protected final Map<string> caches = new StrictMap<cache>("Caches collection"); protected final Map<string> resultMaps = new StrictMap<resultmap>("Result Maps collection"); protected final Map<string> parameterMaps = new StrictMap<parametermap>("Parameter Maps collection"); protected final Map<string> keyGenerators = new StrictMap<keygenerator>("Key Generators collection");/<keygenerator>/<string>/<parametermap>/<string>/<resultmap>/<string>/<cache>/<string>/<mappedstatement>/<string>/<code>
其中最主要的也是相對複雜的有如下兩個(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的代碼
<code> SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is); System.out.println(sqlSessionFactory.getConfiguration());/<code>
再來看SqlSessionFactoryBuilder.build()方法:
<code> 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); }/<code>
加載具體配置
<code> 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); } }/<code>
加載properties節點
<code> 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 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); } }/<code>
從這個方法中可以看出配置規則
- 可以設置url或resource屬性從外部文件中加載一個properties文件
- 可以通過property子節點進行配置,如果子節點屬性的key與外部文件的key重複的話,子節點的將被覆
- 通過編程方式定義的屬性最後加載,優先級最高:
<code> public SqlSessionFactory build(InputStream inputStream, Properties properties)/<code>
properties配置示例
<code> <properties> <property> <property> /<properties>/<code>
這裡加載的主要是給後面的配置作為變量使用!
加載別名
<code> 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); } } } } }/<code>
再看 TypeAliasRegistry源碼,發現mybatis已經為定義了很多別名,方便以後的配置
<code> 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); }/<code>
還有一個加載通過別名加載class的方法
<code> publicClass /<code>resolveAlias(String string) { try { if (string == null) return null; String key = string.toLowerCase(Locale.ENGLISH); // issue #748 Class value; if (TYPE_ALIASES.containsKey(key)) { //如果是別名,直接從註冊表裡返回 value = (Class ) TYPE_ALIASES.get(key); } else { value = (Class ) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } }
加載Mapper配置文件
<code> 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."); } } } } }/<code>
一個Mapper的配置文件最終會由XMLMapperBuilder對象解析加載到Configuration對象中。XMLMapperBuilder的解析過程中XMLConfigBuilder解析過程差不多,以後再詳細分析!
加載其他配置項
還有一些配置項這裡沒有講到,如:插件/攔截器、對象工廠、setting項。這些的加載都比較簡單,只要花點心裡就可以看明白。在以後分析代碼過程中,一定會看到這裡配置,到時再進一步研究,不過可以肯定這裡配置很多情況下都是使用默認的值。
閱讀更多 IT技術編程圈 的文章