Mybatis源码分析(2) -- Configuration解析

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>,我们在后续的篇章中再详细说明,这一篇就到此为止了。

总结

  1. Mybatis将xml配置文件解析成Configuration对象,是通过XmlConfigBuilder进行解析的,在创建SqlSessionFactory的时候就已经解析完成了。
  2. <propertites>的解析具有先后顺序:构造参数传入 > resource、url资源配置 > <propertity>配置。
  3. <typeAliases>类型别名,可以通过在类上设置@Alias注解,以及扫包的形式注册,TypeAliasRegistry负责所有别名的注册和获取。
  4. <typeHandlers>类型句柄用于预处理语句设值和结果集类型转换,可以使用@MappedTypes注解javaType,@MappedJdbcTypes注解jdbcType。TypeHandlerRegistry负责所有类型句柄的注册和获取。
  5. <mappers>映射语句的解析是最终是交给MapperAnnotationBuilder和XMLMapperBuilder进行解析的。

转载于:https://my.oschina.net/xiaoqiyiye/blog/1625341

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值