1. 程式人生 > >mybatis原始碼解析之Configuration載入(一)

mybatis原始碼解析之Configuration載入(一)

概要

上一篇,我們主要搭建了一個簡單的環境,這邊我們主要來分析下mybatis是如何來載入它的配置檔案Configuration.xml的。

分析

 1 public class App {
 2     public static void main(String[] args) {
 3         try {
 4             InputStream inputStream = Resources.getResourceAsStream("configuration/Configuration.xml");
 5             SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream); 6 SqlSession session = sqlSessionFactory.openSession(); 7 //寫法一 8 //NewsVO newsVo = session.selectOne("com.mybatis.read.dao.NewsMapper.getNews", 40); 9 //寫法二 10 NewsMapper newsMapper = session.getMapper(NewsMapper.class
); 11 NewsVO newsVo = newsMapper.getNews(40); 12 System.out.println(newsVo.toString()); 13 } catch (IOException e) { 14 e.printStackTrace(); 15 } 16 } 17 }

我們先看下第4行程式碼,看起來很簡單,利用Resource載入指定路徑下的檔案,獲取輸入流,具體的程式碼實現為:

 1   InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
2 for (ClassLoader cl : classLoader) { 3 if (null != cl) { 4 5 // try to find the resource as passed 6 InputStream returnValue = cl.getResourceAsStream(resource); 7 8 // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource 9 if (null == returnValue) { 10 returnValue = cl.getResourceAsStream("/" + resource); 11 } 12 13 if (null != returnValue) { 14 return returnValue; 15 } 16 } 17 } 18 return null; 19 }

 

可以看出來,其內部載入resource的方式還是通過classloader.getResouceAsStream。

獲取到輸入流之後呢,我們看到是通過SqlSessionFactoryBuilder的build方法去載入的,我們可以繼續跟進去,可以發現SqlSessionFactoryBuilder中使用了多型,主要有這麼兩個方法,具體如下:

1.通過reader的字元流載入配置檔案,返回SqlSessionFactory

 1   public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
 2     try {
 3       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
 4       return build(parser.parse());
 5     } catch (Exception e) {
 6       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 7     } finally {
 8       ErrorContext.instance().reset();
 9       try {
10         reader.close();
11       } catch (IOException e) {
12         // Intentionally ignore. Prefer previous error.
13       }
14     }
15   }

 

2.通過inputStream的位元組流載入配置檔案,獲取SqlSessionFactory

 1   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
 2     try {
 3       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
 4       return build(parser.parse());
 5     } catch (Exception e) {
 6       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 7     } finally {
 8       ErrorContext.instance().reset();
 9       try {
10         inputStream.close();
11       } catch (IOException e) {
12         // Intentionally ignore. Prefer previous error.
13       }
14     }
15   }

 

我們的main方法中使用的是inputStream的方式,我們就來看下方式二的程式碼。解析Configuration.xml的程式碼就在第三行XMLConfigBuilder的構造方法中,我們繼續跟進去,

1   public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
2     this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
3   }

 

很明顯,這裡關鍵是第二行的XPathParser,我繼續跟進去看一下它的在構造中幹了什麼?

1   public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
2     commonConstructor(validation, variables, entityResolver);
3     this.document = createDocument(new InputSource(inputStream));
4   }

 

第二行程式碼commonConstructor,

1   private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
2     this.validation = validation;
3     this.entityResolver = entityResolver;
4     this.variables = variables;
5     XPathFactory factory = XPathFactory.newInstance();
6     this.xpath = factory.newXPath();
7   }

初始化XPathParser的類變數validation,variables,entityResolver,並獲取一個xpath的例項。

再看第三行程式碼createDocument方法:

 1   private Document createDocument(InputSource inputSource) {
 2     // important: this must only be called AFTER common constructor
 3     try {
 4       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 5       factory.setValidating(validation);
 6 
 7       factory.setNamespaceAware(false);
 8       factory.setIgnoringComments(true);
 9       factory.setIgnoringElementContentWhitespace(false);
10       factory.setCoalescing(false);
11       factory.setExpandEntityReferences(true);
12 
13       DocumentBuilder builder = factory.newDocumentBuilder();
14       builder.setEntityResolver(entityResolver);
15       builder.setErrorHandler(new ErrorHandler() {
16         @Override
17         public void error(SAXParseException exception) throws SAXException {
18           throw exception;
19         }
20 
21         @Override
22         public void fatalError(SAXParseException exception) throws SAXException {
23           throw exception;
24         }
25 
26         @Override
27         public void warning(SAXParseException exception) throws SAXException {
28         }
29       });
30       return builder.parse(inputSource);
31     } catch (Exception e) {
32       throw new BuilderException("Error creating document instance.  Cause: " + e, e);
33     }
34   }

 

第5到11行程式碼,主要設定DocumentBuilderFactory中一些引數的值,具體含義如下:

  • setValidating表示是否驗證xml檔案,這個驗證是DTD驗證
  • setNamespaceAware表示是否支援xml名稱空間
  • setIgnoringComments表示是否忽略註釋
  • setIgnoringElementContentWhitespace表示是否忽略元素中的空白
  • setCoalescing表示是否將CDATA節點轉換為Text節點,並將其附加到相鄰(如果有)的Text節點
  • setExpandEntityReferences表示是否擴充套件實體引用節點

第13行程式碼從DocumentBuilderFactory中獲取DocumentBuilder例項,第14行程式碼設定一個實體解析器,第15到29行程式碼設定一個錯誤處理器。

再看下第30行的parse方法,

 1     public Document parse(InputSource is) throws SAXException, IOException {
 2         if (is == null) {
 3             throw new IllegalArgumentException(
 4                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
 5                 "jaxp-null-input-source", null));
 6         }
 7         if (fSchemaValidator != null) {
 8             if (fSchemaValidationManager != null) {
 9                 fSchemaValidationManager.reset();
10                 fUnparsedEntityHandler.reset();
11             }
12             resetSchemaValidator();
13         }
14         domParser.parse(is);
15         Document doc = domParser.getDocument();
16         domParser.dropDocumentReferences();
17         return doc;
18     }

 

這邊就是用DocumentBuilder將inputStream載入成org.w3c.dom.Document物件返回,並且儲存在XPathParser中。

我們前面看了這麼多,其實就是分析了這麼一句程式碼 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 得到了org.w3c.dom.Document物件,那麼又是怎麼得到Java物件的呢?

我們回到SqlSessionFactoryBuilder的build方法:

 1   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
 2     try {
 3       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
 4       return build(parser.parse());
 5     } catch (Exception e) {
 6       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 7     } finally {
 8       ErrorContext.instance().reset();
 9       try {
10         inputStream.close();
11       } catch (IOException e) {
12         // Intentionally ignore. Prefer previous error.
13       }
14     }
15   }

 

之前我們分析了第3行程式碼,接下來我們看一下第4行程式碼,

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   }

 

這個方法裡面首先判斷有沒有解析過,如果解析過了,直接丟擲異常,重點看下第6行程式碼,這裡的parser就是Xpathparse,看下它的evalNode方法:

 1   public XNode evalNode(String expression) {
 2     return evalNode(document, expression);
 3   }
 4 
 5   public XNode evalNode(Object root, String expression) {
 6     Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
 7     if (node == null) {
 8       return null;
 9     }
10     return new XNode(this, node, variables);
11   }
12 
13   private Object evaluate(String expression, Object root, QName returnType) {
14     try {
15       return xpath.evaluate(expression, root, returnType);
16     } catch (Exception e) {
17       throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
18     }
19   }

 

主要就是解析configuration.xml中configuration開始的節點,並將結果放到XNode中返回,接下來的分別的去獲取XNode中的內容。看下第6行parseConfiguration方法,

 1     try {
 2       //issue #117 read properties first
 3       propertiesElement(root.evalNode("properties"));
 4       Properties settings = settingsAsProperties(root.evalNode("settings"));
 5       loadCustomVfs(settings);
 6       typeAliasesElement(root.evalNode("typeAliases"));
 7       pluginElement(root.evalNode("plugins"));
 8       objectFactoryElement(root.evalNode("objectFactory"));
 9       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
10       reflectorFactoryElement(root.evalNode("reflectorFactory"));
11       settingsElement(settings);
12       // read it after objectFactory and objectWrapperFactory issue #631
13       environmentsElement(root.evalNode("environments"));
14       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
15       typeHandlerElement(root.evalNode("typeHandlers"));
16       mapperElement(root.evalNode("mappers"));
17     } catch (Exception e) {
18       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
19     }
20   }

 

這個就是逐個解析configuration.xml中configuration的子節點,並設定到對應的屬性中。我們並不一個一個的去看,因為有些標籤並不常用。

這裡還要在看個東西,就是XMLConfigBuilder這個類父類,BaseBuilder,它有幾個類成員,

1   protected final Configuration configuration;
2   protected final TypeAliasRegistry typeAliasRegistry;
3   protected final TypeHandlerRegistry typeHandlerRegistry;
Configuration物件,就是最終將配置檔案轉化成java的那個物件,包含所有的配置資訊,TypeAliasRegistry,這個用來註冊配置了別名的類,供後面使用,後續會講到。

properties解析

首先,我們來看下,我們在configuration.xml中的properties標籤是什麼樣子的?

1 <properties resource="properties/db.properties" />

 具體的解析程式碼如下:

 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   }

 很明顯,從第4行和第5行程式碼看出,<properties>標籤下不能同時指定"resource"屬性和"url"屬性。接下來的9到19行程式碼,載入我們制定的.properties檔案,並將結果儲存到XPathParser和Configuration的Variable中去

settings解析

解析程式碼如下:

 1   private Properties settingsAsProperties(XNode context) {
 2     if (context == null) {
 3       return new Properties();
 4     }
 5     Properties props = context.getChildrenAsProperties();
 6     // Check that all settings are known to the configuration class
 7     MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
 8     for (Object key : props.keySet()) {
 9       if (!metaConfig.hasSetter(String.valueOf(key))) {
10         throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
11       }
12     }
13     return props;
14   }

 

首先如果沒有<settings>標籤,則返回一個新的Properties物件。不為空的話,第5行程式碼,獲取<settings>標籤的所有子標籤,第7到12行程式碼,這個一次看的時候比較難懂啊,其實很簡單,就是驗證<settings>的子標籤當中的name是不是胡亂寫的,具體是怎麼驗證的呢?簡單來說就是,<setting>的name屬性對應的值,必須在Configuration類有相應的Setter,比如設定了一個屬性useGenerateKeys方法,那麼必須在Configuration類中有setUseGenerateKeys方法才行

這邊這部分只是解析了<settings>,具體用處後面繼續講。

類別名解析

 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   }

 

這個方法是解析<typeAliases>標籤,首先我們看第4行和第7行的if......else......,可以看出,<typeAliases>標籤下不能同時出現<package>和<typeAlias>,我們首先來看<package>標籤的情況,看下第6行程式碼,獲取到name後,直接就呼叫registerAliases方法進行註冊,程式碼如下:

 1   public void registerAliases(String packageName){
 2     registerAliases(packageName, Object.class);
 3   }
 4 
 5   public void registerAliases(String packageName, Class<?> superType){
 6     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
 7     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
 8     Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
 9     for(Class<?> type : typeSet){
10       // Ignore inner classes and interfaces (including package-info.java)
11       // Skip also inner classes. See issue #6
12       if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
13         registerAlias(type);
14       }
15     }
16   }

 

6到8行程式碼,在package目錄下尋找.class檔案,並獲取對應的class檔案,第9到14行程式碼,根據獲取的class,排除匿名類,介面,成員類,然後呼叫registerAlias進行註冊:

1   public void registerAlias(Class<?> type) {
2     String alias = type.getSimpleName();
3     Alias aliasAnnotation = type.getAnnotation(Alias.class);
4     if (aliasAnnotation != null) {
5       alias = aliasAnnotation.value();
6     } 
7     registerAlias(alias, type);
8   }

 

這段程式碼主要作用是獲取class的simpleName,也就是不包含類路徑的那個名字,3到6行程式碼,獲取類上面的Alias註解的value值,不為空的話就用這個當做類的別名。第7行程式碼進行註冊:

 1   public void registerAlias(String alias, Class<?> value) {
 2     if (alias == null) {
 3       throw new TypeException("The parameter alias cannot be null");
 4     }
 5     // issue #748
 6     String key = alias.toLowerCase(Locale.ENGLISH);
 7     if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
 8       throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
 9     }
10     TYPE_ALIASES.put(key, value);
11   }

 

第2到4行程式碼,判斷別名不能為空,第6行程式碼,將別名全部小寫,7到9行程式碼,判斷 TYPE_ALIASES這個hashMap中是否已經存在這樣的別名或者這樣的類,第10行程式碼,將類加到hashMap中,供後續使用。

如果使用的是<typeAlias>標籤的話:

 1           String alias = child.getStringAttribute("alias");
 2           String type = child.getStringAttribute("type");
 3           try {
 4             Class<?> clazz = Resources.classForName(type);
 5             if (alias == null) {
 6               typeAliasRegistry.registerAlias(clazz);
 7             } else {
 8               typeAliasRegistry.registerAlias(alias, clazz);
 9             }
10           } catch (ClassNotFoundException e) {
11             throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
12           }

 

第4行程式碼,如果一直跟進去的話,你就會發現,最後實際上是呼叫classloder按型別載入初始化類。第5到9行程式碼,根據標籤中的<alias> 是否設定,去分別註冊類,這兩個方法上面都看過了,不在贅述。

剛才跟程式碼的時候,其實可以發現這裡面已經有很多預設的typeAlias,Configuration類中有如下部分:

 1     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
 2     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
 3 
 4     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
 5     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
 6     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
 7 
 8     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
 9     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
10     typeAliasRegistry.registerAlias("LRU", LruCache.class);
11     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
12     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
13 
14     typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
15 
16     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
17     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
18 
19     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
20     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
21     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
22     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
23     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
24     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
25     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
26 
27     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
28     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
29 
30     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
31     languageRegistry.register(RawLanguageDriver.class);

TypeAliasRegistry類中有如下部分:

 1     registerAlias("string", String.class);
 2 
 3     registerAlias("byte", Byte.class);
 4     registerAlias("long", Long.class);
 5     registerAlias("short", Short.class);
 6     registerAlias("int", Integer.class);
 7     registerAlias("integer", Integer.class);
 8     registerAlias("double", Double.class);
 9     registerAlias("float", Float.class);
10     registerAlias("boolean", Boolean.class);
11 
12     registerAlias("byte[]", Byte[].class);
13     registerAlias("long[]", Long[].class);
14     registerAlias("short[]", Short[].class);
15     registerAlias("int[]", Integer[].class);
16     registerAlias("integer[]", Integer[].class);
17     registerAlias("double[]", Double[].class);
18     registerAlias("float[]", Float[].class);
19     registerAlias("boolean[]", Boolean[].class);
20 
21     registerAlias("_byte", byte.class);
22     registerAlias("_long", long.class);
23     registerAlias("_short", short.class);
24     registerAlias("_int", int.class);
25     registerAlias("_integer", int.class);
26     registerAlias("_double", double.class);
27     registerAlias("_float", float.class);
28     registerAlias("_boolean", boolean.class);
29 
30     registerAlias("_byte[]", byte[].class);
31     registerAlias("_long[]", long[].class);
32     registerAlias("_short[]", short[].class);
33     registerAlias("_int[]", int[].class);
34     registerAlias("_integer[]", int[].class);
35     registerAlias("_double[]", double[].class);
36     registerAlias("_float[]", float[].class);
37     registerAlias("_boolean[]", boolean[].class);
38 
39     registerAlias("date", Date.class);
40     registerAlias("decimal", BigDecimal.class);
41     registerAlias("bigdecimal", BigDecimal.class);
42     registerAlias("biginteger", BigInteger.class);
43     registerAlias("object", Object.class);
44 
45     registerAlias("date[]", Date[].class);
46     registerAlias("decimal[]", BigDecimal[].class);
47     registerAlias("bigdecimal[]", BigDecimal[].class);
48     registerAlias("biginteger[]", BigInteger[].class);
49     registerAlias("object[]", Object[].class);
50 
51     registerAlias("map", Map.class);
52     registerAlias("hashmap", HashMap.class);
53     registerAlias("list", List.class);
54     registerAlias("arraylist", ArrayList.class);
55     registerAlias("collection", Collection.class);
56     registerAlias("iterator", Iterator.class);
57 
58     registerAlias("ResultSet", ResultSet.class);

 

 對於這些資料,我們可以直接使用registerAlias方法的第一個引數對應的字串而不需要定義這些typeAlias。

上面我記得在解析<settings>標籤的時候,最後返回的propertis並沒有用,我們來看下parseConfiguration的settingsElement方法:

 1   private void settingsElement(Properties props) throws Exception {
 2     configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
 3     configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
 4     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
 5     configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
 6     configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
 7     configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
 8     configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
 9     configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
10     configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
11     configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
12     configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
13     configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
14     configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
15     configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
16     configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
17     configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
18     configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
19     configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
20     configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
21     configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
22     configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
23     configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
24     configuration.setLogPrefix(props.getProperty("logPrefix"));
25     @SuppressWarnings("unchecked")
26     Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));
27     configuration.setLogImpl(logImpl);
28     configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
29   }

 這個方法就是講<settings>標籤中設定的屬性設定到configuration中,有些欄位如果沒有設定的話,設定預設值。

configuation.xml的解析我們先講到這裡,下一篇我們介紹剩下的兩個比較重要的標籤<environments>和<mapper>的解析。