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); } }
结构很简单,里面就是一个 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(); } ...其他方法先省略 }
当创建 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); }
所以初始化 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); }
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); } }
数据结构也很明了,在 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()); } }
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(); } }
所以 TYPE_HANDLER_MAP 的 key 是 Type 类型,但是他的 value 是一个 Map<JdbcType, TypeHandler<?>> map,看起来很奇怪但是仔细想想很合理,有多个 jdbc 类型可以转换成相同的 java 类型,那么有可能就需要不同的 TypeHandler。
ALL_TYPE_HANDLERS_MAP 就是存放所有的 typeHandler
TypeHandlerRegistry 类当中的重载的 register 就不仔细分析了。
---EOF---