您的位置:首页 > 产品设计 > UI/UE

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.html

Configuration的属性

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项。这些的加载都比较简单,只要花点心里就可以看明白。在以后分析代码过程中,一定会看到这里配置,到时再进一步研究,不过可以肯定这里配置很多情况下都是使用默认的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java jdbc mybatis sql