mybatis 源碼分析之 BaseBuilder
來自: http://renchx.com/mybatis2
解析 typeAliases
在 XMLConfigBuilder 當中的 parseConfiguration 方法當中 typeAliasesElement(root.evalNode("typeAliases")); 來解析 typeAliases,下面是詳細代碼:
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
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);
}
}
}
}
}
對應的 xml 格式如下:
<typeAliases>
<package name="com.rcx.test"/>
<typeAlias type="com.rcx.User" alias="User"/>
<typeAlias type="com.rcx.Book" />
</typeAliases>
當是 package 的時候把這個包下面的所有類都掃描出來當成別名。下面的兩種方式是指定具體的 class,區別就是指定了別名,和沒指定別名,沒指定別名的時候使用 class.getSimpleName(); 來當作別名。
下面看一下 TypeAliasRegistry 的內部數據結構:
public class TypeAliasRegistry {
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
public TypeAliasRegistry() {
registerAlias("string", String.class);
...省略了內置的基本類似別名
registerAlias("iterator", Iterator.class);
}
}</code></pre>
結構很簡單,里面就是一個 HashMap 然后對這個 map 進行各種操作。
BaseBuilder
上面提到的 XMLConfigBuilder 繼承于 BaseBuilder,而且其他所有解析 XML 的 Builder 都會繼承這個抽象類。
看下 BaseBuilder 當中的結構:
public abstract class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
...其他方法先省略
}</code></pre>
當創建 BaseBuilder 子類實例的時候,需要傳入 Configuration 的實例,入口的解析器 Builder 就是 XMLConfigBuilder,看下它的構造方法:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
因為在 SqlSessionFactoryBuilder 類當中 build 生成 SqlSessionFactory 的時候是調用了 XMLConfigBuilder 的 parse 方法生成 Configuration 實例,代碼如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
//省略了 try 和 catch
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}</code></pre>
所以初始化 Configuration 實例的流程就清晰了:
- 先創建 XMLConfigBuilder 實例,然后創建了 Configuration 實例
- 在 XMLConfigBuilder 當中把 Configuration 的每一個節點都解析然后設置進去
- 最后返回 Configuration 實例,完成初始化工作
當然 BaseBuilder 當中還包含了解析 Builder 的一些通用的工具方法,下面簡單列幾個:
protected Pattern parseExpression(String regex, String defaultValue) {
return Pattern.compile(regex == null ? defaultValue : regex);
}
protected Boolean booleanValueOf(String value, Boolean defaultValue) {
return value == null ? defaultValue : Boolean.valueOf(value);
}
protected Integer integerValueOf(String value, Integer defaultValue) {
return value == null ? defaultValue : Integer.valueOf(value);
}</code></pre>
XMLConfigBuilder 的具體工作
要想了解 XMLConfigBuilder 的具體工作就要看它的 parseConfiguration 方法:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));//解析內置的屬性
typeAliasesElement(root.evalNode("typeAliases"));//解析別名
pluginElement(root.evalNode("plugins"));//解析插件
objectFactoryElement(root.evalNode("objectFactory"));//解析對象工廠
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
settingsElement(root.evalNode("settings"));//解析默認設置
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));//解析環境
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));//解析類型處理器
mapperElement(root.evalNode("mappers"));//解析用戶定義的 mappers
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
從上面可以看出來,主要是對 config.xml 進行解析,剛才分析了別名的解析比較簡單,比較復雜的是 mappers 的解析。
下面挑一些進行簡單分析 解析插件 :
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.addInterceptor(interceptorInstance);
}
}
}
對應的 xml 如下:
<plugins>
<plugin interceptor="com.rcx.TestInterceptor">
<property name="" value=""/>
<property name="" value=""/>
</plugin>
</plugins>
不需要過多的解釋,主要看 configuration.addInterceptor(interceptorInstance); 在 configuration 當中的數據結構。
protected final InterceptorChain interceptorChain = new InterceptorChain();
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}</code></pre>
數據結構也很明了,在 List 當中按順序存放所有的插件。
* 解析 typeHandler *
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
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);
}
}
}
}
}
對應的 XML 如下:
<typeHandlers>
<package name="com.rcx.test"/>
<typeHandler handler="" javaType="" jdbcType=""/>
</typeHandlers>
無論是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,還是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。
package 是在這個包下的都掃描成 typeHandler,typeHandler 標簽的具體分析,下面看 typeHandlerRegistry 的數據結構:
// 在 Configuration 當中的屬性
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
public final class TypeHandlerRegistry {
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
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);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
...//省略
register(char.class, new CharacterTypeHandler());
}
}</code></pre>
JDBC_TYPE_HANDLER_MAP 這個 map 存放的是 JDBC 類型的 TypeHandler 在 TypeHandlerRegistry 構造方法里面已經進行了初始化:
public TypeHandlerRegistry() {
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
。。。省略
}
之所以創建的是 EnumMap 是因為數據庫類型一定小于 64 種,使用 EnumMap 會更快、也節省空間。
TYPE_HANDLER_MAP 最個 map 是存放我們自定義的類型的轉換的 map,我們自定義的 TypeHandler 都要繼承于 BaseTypeHandler 這個類,BaseTypeHandler 繼承于 TypeReference,繼承于 TypeReference 的主要作用就是可通過反射拿到具體的 TypeHandler 的泛型 class。
public abstract class TypeReference<T> {
private final Type rawType;
protected TypeReference() {
rawType = getSuperclassTypeParameter(getClass());
}
Type getSuperclassTypeParameter(Class<?> clazz) {
Type genericSuperclass = clazz.getGenericSuperclass();
if (genericSuperclass instanceof Class) {
// try to climb up the hierarchy until meet something useful
if (TypeReference.class != genericSuperclass) {
return getSuperclassTypeParameter(clazz.getSuperclass());
}
throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "
+ "Remove the extension or add a type parameter to it.");
}
Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
// TODO remove this when Reflector is fixed to return Types
if (rawType instanceof ParameterizedType) {
rawType = ((ParameterizedType) rawType).getRawType();
}
return rawType;
}
public final Type getRawType() {
return rawType;
}
@Override
public String toString() {
return rawType.toString();
}
}</code></pre>
所以 TYPE_HANDLER_MAP 的 key 是 Type 類型,但是他的 value 是一個 Map<JdbcType, TypeHandler<?>> map,看起來很奇怪但是仔細想想很合理,有多個 jdbc 類型可以轉換成相同的 java 類型,那么有可能就需要不同的 TypeHandler。
ALL_TYPE_HANDLERS_MAP 就是存放所有的 typeHandler
TypeHandlerRegistry 類當中的重載的 register 就不仔細分析了。
---EOF---
</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></article>