厨房小码农

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>的解析。

posted on 2018-12-18 15:49  厨房小码农  阅读(698)  评论(1编辑  收藏  举报

导航