Mybatis源码分析(2) -- Configuration解析
前面分析了SqlSession的创建,SqlSession使用了默认实现DefaultSqlSession的构造方法DefaultSqlSession(Configuration configuration, Executor executor)来创建了一个SqlSession实例。那么,Configuration是怎么解析Xml配置文件的呢,下面我们开始分析这个问题。
其实,在使用SqlSessionFactoryBuilder.build(...)时,就已经创建好了Configuration这个对象,那么,这个对象是怎么生成的呢? 是的,通过调用XMLConfigBuilder的parse()方法可以创建Configuration对象。 分析的入口就是XMLConfigBuilder的构造方法和parse()方法。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
Configuration config = parser.perse();
return build(config);
}
在分析前做一些简单的说明。 解析的过程是基于javas.xml.xpath包提供的xpath解析,XPathParser简易的封装了XPath,XNode对Node进行了简易包装。代码中所有的节点都是XNode形式。
先看看XMLConfigBuilder的构造方法XMLConfigBuilder(inputStream, environment, properties)
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
//方法传入属性,优先级是最高的,在后面分析<properties>时会说明
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
从构造方法可以看出来,首先创建了一个空的Configuration实例,然后设置了props属性文件,以及一个XPathParser实例。
XMLConfigBuilder构造好之后,就可以调用parse()方法创建Configuration对象,就是将这个Configuration对象传给了SqlSessionFactory的构造函数。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 先解析出Mybatis的根节点<configuration>
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//分别解析各个子节点
propertiesElement(root.evalNode("properties"));
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
properties
为了方便看懂源码,我们先把<properties>
的配置写出来,再对比源代码分析:
配置文件
<!-- 注意: resource和url不能同时都配置,这里只是示例可以有这两种方式 -->
<properties resource="jdbc.properties" url="http://www.xxx.xxx/jdbc.properties">
<property name="username" value="root"/>
<property name="password" value="123"/>
</properties>
源代码
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//先获取所有的子节点<property>
Properties defaults = context.getChildrenAsProperties();
//获取resource属性
String resource = context.getStringAttribute("resource");
//获取url属性
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.");
}
//加载resource或url属性资源文件
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);
}
//将所有数据合并后,重新设置到configuration中去
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
属性文件定义优先级: 方法直接传入 > 通过resource、url引用的资源 > property定义
typeAliases
为了方便看懂源码,我们先把<typeAliases>
的配置写出来,再对比源代码分析:
配置文件
<typeAliases>
<!-- 指定具体类型的别名 -->
<typeAlias alias="Goods" type="seven.mybatis.Goods"/>
<!-- 根据包路径扫描到类,然后注册别名 -->
<package name="seven.mybatis">
</typeAliases>
源代码
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//判断是否是<package>节点
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 解析<typeAlias>节点,然后放到别名注册器中去
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);
}
}
}
}
}
解析<typeAliases>
时,不管是<typeAlias>
还是<package>
最终都注册到了TypeAliasRegistry类中,下面分析下TypeAliasRegistry。
public class TypeAliasRegistry {
//保存注册的别名
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
public TypeAliasRegistry() {
//默认注册了很多别名,这些别名都是在写SQL语句时经常用到的
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);
}
/**
* 解析别名,根据别名获取类型
*/
@SuppressWarnings("unchecked")
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) return null;
String key = string.toLowerCase(Locale.ENGLISH);
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);
}
}
/**
* 根据包名注册别名
*/
public void registerAliases(String packageName){
registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType){
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
//进行扫包查找
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
//获取包中的所有class文件
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for(Class<?> type : typeSet){
// 忽略内部类、接口以及package-info.java
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
/**
* 使用类型上的@Alias注解注册别名,如果没有@Alias注解,则使用类名
*/
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
/**
* 为类型注册别名
*/
public void registerAlias(String alias, Class<?> value) {
if (alias == null) throw new TypeException("The parameter alias cannot be null");
String key = alias.toLowerCase(Locale.ENGLISH); // issue #748
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
TYPE_ALIASES.put(key, value);
}
public void registerAlias(String alias, String value) {
try {
registerAlias(alias, Resources.classForName(value));
} catch (ClassNotFoundException e) {
throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
}
}
/**
* 获取所有别名映射集合
*/
public Map<String, Class<?>> getTypeAliases() {
return Collections.unmodifiableMap(TYPE_ALIASES);
}
}
别名的设置很简单,分析到这里就差不多了。
plugins
为了方便看懂源码,我们先把<plugins>
的配置写出来,再对比源代码分析:
配置文件
<plugins>
<plugin interceptor="com.as.interceptor.PageInterceptor">
<property name="databaseType" value="mySql"/>
</plugin>
</plugins>
源代码
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//获取配置的拦截器
String interceptor = child.getStringAttribute("interceptor");
//获取属性
Properties properties = child.getChildrenAsProperties();
//反射获取拦截器实例
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
//添加到Configuration中去
configuration.addInterceptor(interceptorInstance);
}
}
}
objectFactory
为了方便看懂源码,我们先把<objectFactory>
的配置写出来,再对比源代码分析:
配置文件
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="name" value="linya"/>
</objectFactory>
源代码
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
//获取type
String type = context.getStringAttribute("type");
//获取属性
Properties properties = context.getChildrenAsProperties();
//反射获取ObjectFactory实例
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
factory.setProperties(properties);
//添加到Configuration中去
configuration.setObjectFactory(factory);
}
}
settings
为了方便看懂源码,我们先把<settings>
的配置写出来,再对比源代码分析:
配置文件
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
源代码
private void settingsElement(XNode context) throws Exception {
if (context != null) {
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
MetaClass metaConfig = MetaClass.forClass(Configuration.class);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
//在这里我们就可以知道可以设置哪些全局属性了,以及默认值
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
}
environments
为了方便看懂源码,我们先把<environments>
的配置写出来,再对比源代码分析:
配置文件
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
源代码
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
//如果构造参数中没有传environment参数,则从配置文件中获取默认default
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//判断environment、id不能为空,且environment=id就返回true,也就是找到指定的environment节点
if (isSpecifiedEnvironment(id)) {
//解析出事务工厂
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//解析出数据源
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//构建一个Environment.Builder实例,然后调用build(),创建Environment设置到Configuration中去
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
//获取type属性,type属性可以是JDBC|MANAGED
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//根据别名反射创建TransactionFactory实例
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
//获取type属性,Mybatis支持3中数据源工厂Pooled、UnPooled、Jndi
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//反射获取DataSourceFactory实例
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
typeHandlers
<typeHandlers>
是用来配置TypeHandler的,Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。那么,Mybatis为我们实现了哪些TypeHandler呢? 我们怎么自定义实现一个TypeHandler?
为了方便看懂源码,我们先把<typeHandlers>
的配置写出来,再对比源代码分析:
配置文件
<typeHandlers>
<typeHandler javaType="string" jdbcType="varchar" handler=""/>
<package name="seven.xiaoqiyiye.mybatis.typehandlers"/>
</typeHandlers>
源代码
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//解析<package>节点,根据包名扫描
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
//解析<typeHandler>节点,然后注册到TypeHandlerRegistry中去
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
解析<typeHandlers>
时,不管是<typeHandler>
还是<package>
最终都注册到了TypeHandlerRegistry类中,下面分析下TypeHandlerRegistry。
先看看TypeHandlerRegistry的属性和构造方法。
//基本类型
private static final Map<Class<?>, Class<?>> reversePrimitiveMap = new HashMap<Class<?>, Class<?>>() {
private static final long serialVersionUID = 1L;
{
put(Byte.class, byte.class);
put(Short.class, short.class);
put(Integer.class, int.class);
put(Long.class, long.class);
put(Float.class, float.class);
put(Double.class, double.class);
put(Boolean.class, boolean.class);
put(Character.class, char.class);
}
};
//定义JdbcType对应的TypeHandler映射集合
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
//javaType、jdbcType和TypeHandler的映射集合
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
//未知的类型
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
// 所有的TypeHandler类型映射集合
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
public TypeHandlerRegistry() {
//设置各种类型的TypeHandler
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}
在上面我们看到了有几种重载的register()方法:
register(Type javaType, TypeHandler typeHandler);
register(JdbcType jdbcType, TypeHandler typeHandler);
register(Type javaType, JdbcType jdbcType, TypeHandler typeHandler);
//根据包名注册
register(String packageName);
下面我们具体分析下,TypeHandler是如何注册的。
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//判断TypeHandler是否有@MappedJdbcTypes注解,如果有注解则注册时设置jdbcType
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
//可以设置多个jdbcType
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
register(javaType, null, typeHandler);
}
} else {
register(javaType, null, typeHandler);
}
}
public void register(JdbcType jdbcType, TypeHandler<?> handler) {
//直接注册到JDBC_TYPE_HANDLER_MAP中去
JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
//注册到TYPE_HANDLER_MAP中去
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null) {
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
//如果是基本类型,把基本类型也注册进去
if (reversePrimitiveMap.containsKey(javaType)) {
register(reversePrimitiveMap.get(javaType), jdbcType, handler);
}
}
//注册TypeHandler到ALL_TYPE_HANDLERS_MAP中去
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
/**
* 根据包名扫描注册
*/
public void register(String packageName) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
for (Class<?> type : handlerSet) {
//忽略内部类、接口、抽象类
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
register(type);
}
}
}
上面分析完注册,下面我们再看看是如何获取TypeHanler的。
/**
* 根据typeHandler类型获取TypeHandler
*/
public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
return ALL_TYPE_HANDLERS_MAP.get(handlerType);
}
/**
* 根据javaType,jdbcType获取TypeHandler
*/
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
handler = jdbcHandlerMap.get(null);
}
}
if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
handler = new EnumTypeHandler((Class<?>) type);
}
TypeHandler<T> returned = (TypeHandler<T>) handler;
return returned;
}
/**
* 获取所有注册的TypeHandler
*/
public Collection<TypeHandler<?>> getTypeHandlers() {
return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
}
mappers
<mappers>
是用来配置Mybatis的映射文件的,映射文件中说明了映射的SQL语句。
为了方便看懂源码,我们先把<mappers>
的配置写出来,再对比源代码分析:
配置文件
<mappers>
<mapper resource="seven/xiaoqiyiye/mapper/User.xml" />
<package name="seven.xiaoqiyiye.mapper"/>
</mappers>
源代码
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//解析<package>
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//解析<mapper>,属性[resource|url|class]有且仅能设置其中一个
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 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 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.");
}
}
}
}
}
<package>
和<mapper class="">
的解析是一样的,<mapper resource="">
和<mapper url="">
的解析是一样,下面我们分两类进行分析。
mapper class
解析package和class最终都是委派给MapperRegistry#addMapper(Class<T> type)来进行处理的,而在这个方法中最终是通过MapperAnnotationBuilder类来解析的,下面看看源码:
public <T> void addMapper(Class<T> type) {
//只能添加接口
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
//委派给注解构建器,去解析接口上的注解
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
mapper resource
解析resource、url是通过XMLMapperBuilder来进行解析的。我们先简单地看下XMLMapperBuilder#parse()方法,下面看看源码:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
因为内容已经比较多了,关于MapperAnnotationBuilder和XMLMapperBuilder具体是如何解析的<mappers>
,我们在后续的篇章中再详细说明,这一篇就到此为止了。
总结
- Mybatis将xml配置文件解析成Configuration对象,是通过XmlConfigBuilder进行解析的,在创建SqlSessionFactory的时候就已经解析完成了。
<propertites>
的解析具有先后顺序:构造参数传入 > resource、url资源配置 ><propertity>
配置。<typeAliases>
类型别名,可以通过在类上设置@Alias注解,以及扫包的形式注册,TypeAliasRegistry负责所有别名的注册和获取。<typeHandlers>
类型句柄用于预处理语句设值和结果集类型转换,可以使用@MappedTypes注解javaType,@MappedJdbcTypes注解jdbcType。TypeHandlerRegistry负责所有类型句柄的注册和获取。<mappers>
映射语句的解析是最终是交给MapperAnnotationBuilder和XMLMapperBuilder进行解析的。