实习日志(1)mybatis源码-xml解析
流程
public static void main(String[] args) throws IOException {
File file = Resources.getResourceAsFile("mybatis-config.xml");
InputStream is = new FileInputStream(file);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
try (SqlSession session = sqlSessionFactory.openSession()) {
RoleInfoMapper roleInfoMapper = session.getMapper(RoleInfoMapper.class);
List<RoleInfo> roleInfoList = roleInfoMapper.selectAll();
session.commit();
}
}
大致流程如上。
1.xml解析 装配configuration文件
SqlSessionFactoryBuilder().build(is);
方法实际上:
关键代码:
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
.............省略异常处理............
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
关键在于parse方法,解析完成了configuration
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases")); // 完成 configuration 的 typeAlises初始化
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(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")); // 这是最重要的 向mapperRegistry注册,mapperRegistry维护一个map<Class,
// MapperProxyFactory>
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
可以配合着官方文档一起看,我们以解析typeAlisesElement为例,负责解析typeAlises标签
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
// TypeAliseRegistry 内部维护一个hashmap<String, Class<?>> ,并在初始化的时候就往里添加了
// string- String.class
// 通过定义的resloveAlias(String) 可以根据别名返回类型
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// -- 先不看
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
typeAliasRegistry.registerAlias(clazz); // 没有alias 会去找注解,注解没有默认首字母小写
} else {
typeAliasRegistry.registerAlias(alias, clazz); // 加入map
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
另外一个比较重要的是typeHandler,解析流程几乎是一样的
读取标签,注册到configuration的typeHandlerRegistry 对象中。
只是typeHandlerRegistry稍稍复杂一些:
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<>();
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<>();
// 总而言之,维护着 java 类型 -> JDBC 类型 -> 具体实TypeHandler类 和
// TypeHandler.Class -> TypeHandler对象
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
}
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
在 paramerterHandler 负责设置参数 的 时候 就使用了从parameterMapping中取得的 typeHandler,最终的具体设置都交给了typeHandler去执行,同样,结果集的结果处理也是一样的
2.mapper映射文件处理
解析mybatis-config的最后一步就是解析mapper文件,解析完会向configuration注册mapper.class - 和能够创建mapper代理对象的工厂。对mapper文件的解析会调用 另外一个XMLMapperBuilder来进行解析,关键代码:
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
} // 先处理cache-ref再处理cache
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析resultMap 重中之重
sqlElement(context.evalNodes("/mapper/sql")); // 这里要完成sql的解析,放入一个map<string,sqlNode>
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
分为 几个大操作,解析cache,resultMap,sql, update|select|insert|delete
cache先放一放,
重点看一下resultMap的解析和增删该查的解析:
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType")))); // 属性resultType javaType ofType type一个意思
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 会覆盖全局,默认unset
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<ResultFlag>(); // 封装每一个result对象
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); // 会完成一个result或者id标签的所有属性解析工作
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
完成后同样要向configuration注册,在一个Map<String, ResultMap>,
被使用是在defaultResultSetHandler中 ,用于创建结果对象
增删该查标签的处理 与之类似,最终会注册到configuration的对象中
Map<String, MappedStatement> mappedStatements
具体执行的时候,由此为起点。