mybatis-xml解析
阿新 • • 發佈:2018-12-06
實習日誌(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
具體執行的時候,由此為起點。