根据mybatis配置加载流程
1 private void parseConfiguration(XNode root) { 2 try { 3 //解析子节点的properties文件 4 propertiesElement(root.evalNode("properties")); 5 //加载用户自定义配置 6 Properties settings = settingsAsProperties(root.evalNode("settings")); 7 //加载vfs虚拟文件系统配置 8 loadCustomVfs(settings); 9 // 解析子节点typeAliases 别名 10 typeAliasesElement(root.evalNode("typeAliases")); 11 //解析子节点plugins 插件 12 pluginElement(root.evalNode("plugins")); 13 //解析子节点objectFactory mybatis为结果创建对象时都会用到objectFactory 14 objectFactoryElement(root.evalNode("objectFactory")); 15 //解析子节点的封装对象 16 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 17 reflectorFactoryElement(root.evalNode("reflectorFactory")); 18 //解析settings标签的文件配置 19 settingsElement(settings); 20 //配置运行环境 21 environmentsElement(root.evalNode("environments")); 22 //解析子节点配置数据库供应商 23 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 24 //解析对象类型处理器,处理Java的pojo类型与数据库类型的映射 25 typeHandlerElement(root.evalNode("typeHandlers")); 26 //解析子节点的mappers 27 mapperElement(root.evalNode("mappers")); 28 } catch (Exception e) { 29 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 30 } 31 }
mapperElement(root.evalNode("mappers"));主要解决mappers标签的配置解析,在mybatis中<mappers>写法为:
1 <!--加载mapper映射 如果将和spring整合后,可以使用整合包中提供的mapper扫描器,此处的mappers不用配置了。 --> 2 <mappers> 3 <!-- 通过resource引用mapper的映射文件 --> 4 <!--<mapper resource="com/ssc/demo/mybatis/mapper/SubjectMapper.xml"/>--> 5 <!-- <mapper resource="mapper/UserMapper.xml" /> --> 6 <!--<mapper url="file:///D:workspaceideagitdemo_01srcmainjavacomsscdemomybatismapperSubjectMapper.xml"/>--> 7 <!-- 通过class引用mapper接口 class:配置mapper接口全限定名 要求:需要mapper.xml和mapper.java同名并且在一个目录 8 中 --> 9 <!--<mapper class="com.ssc.demo.mybatis.dao.SubjectDao"/>--> 10 <!-- 批量mapper配置 通过package进行自动扫描包下边的mapper接口, 要求:需要mapper.xml和mapper.java同名并且在一个目录 11 中 --> 12 <package name="com.ssc.demo.mybatis.mapper"/> 13 </mappers>
mapperElement(root.evalNode("mappers"));方法源码为:
1 private void mapperElement(XNode parent) throws Exception { 2 if (parent != null) { 3 for (XNode child : parent.getChildren()) { 4 //package扫描方式 5 if ("package".equals(child.getName())) { 6 String mapperPackage = child.getStringAttribute("name"); 7 configuration.addMappers(mapperPackage); 8 } else { 9 String resource = child.getStringAttribute("resource"); 10 String url = child.getStringAttribute("url"); 11 String mapperClass = child.getStringAttribute("class"); 12 if (resource != null && url == null && mapperClass == null) { 13 //resource方式 14 ErrorContext.instance().resource(resource); 15 InputStream inputStream = Resources.getResourceAsStream(resource); 16 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); 17 mapperParser.parse(); 18 } else if (resource == null && url != null && mapperClass == null) { 19 //URL方式 20 ErrorContext.instance().resource(url); 21 InputStream inputStream = Resources.getUrlAsStream(url); 22 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); 23 mapperParser.parse(); 24 } else if (resource == null && url == null && mapperClass != null) { 25 //class 方式 26 Class<?> mapperInterface = Resources.classForName(mapperClass); 27 configuration.addMapper(mapperInterface); 28 } else { 29 throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); 30 } 31 } 32 } 33 } 34 }
通过以上方法得知,四种方式对应两种不同形式的mapper配置解析分别为configuration.addMappers(mapperPackage);和 mapperParser.parse();
configuration.addMappers(mapperPackage)源码为:
1 public <T> void addMapper(Class<T> type) { 2 mapperRegistry.addMapper(type); 3 }
1 public <T> void addMapper(Class<T> type) { 2 //判断这类是否是接口类型 3 if (type.isInterface()) { 4 //判断是否已加载 5 if (hasMapper(type)) { 6 throw new BindingException("Type " + type + " is already known to the MapperRegistry."); 7 } 8 boolean loadCompleted = false; 9 try { 10 knownMappers.put(type, new MapperProxyFactory<T>(type)); 11 // It's important that the type is added before the parser is run 12 // otherwise the binding may automatically be attempted by the 13 // mapper parser. If the type is already known, it won't try. 14 //构造一个MapperAnnotationBuilder对象解析这个类 15 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); 16 parser.parse(); 17 loadCompleted = true; 18 } finally { 19 if (!loadCompleted) { 20 knownMappers.remove(type); 21 } 22 } 23 } 24 }
parser.parse();方法源码为:
1 public void parse() { 2 String resource = type.toString(); 3 //判断是否已加载此 配置的例如(USERDAO) 接口 4 if (!configuration.isResourceLoaded(resource)) { 5 // 加载与此类同名的xml类型mapper文件 6 loadXmlResource(); 7 //将该资源加入到全局配置 8 configuration.addLoadedResource(resource); 9 //设置当前命名空间 10 assistant.setCurrentNamespace(type.getName()); 11 //解析二级缓存 12 parseCache(); 13 parseCacheRef(); 14 //获取类中的所以的方法 15 Method[] methods = type.getMethods(); 16 for (Method method : methods) { 17 try { 18 // 判断该方法是否是桥接方法,桥接方法指的是由编译器自动生成的方法 19 if (!method.isBridge()) { 20 //解析statement 21 parseStatement(method); 22 } 23 } catch (IncompleteElementException e) { 24 //将异常解析的方法加入一个List中 25 configuration.addIncompleteMethod(new MethodResolver(this, method)); 26 } 27 } 28 } 29 //解析挂起的方法 30 parsePendingMethods(); 31 }
loadXmlResource();是加载同级包下面的同名的xml配置文件,其源码为
1 private void loadXmlResource() { 2 // 判断此资源是否已经加载,防止加载两次 3 if (!configuration.isResourceLoaded("namespace:" + type.getName())) { 4 //将此类的全限类名的路径替换 5 String xmlResource = type.getName().replace('.', '/') + ".xml"; 6 InputStream inputStream = null; 7 try { 8 //加载同名的xml类型的mapper文件 9 inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource); 10 } catch (IOException e) { 11 // ignore, resource is not required 12 } 13 if (inputStream != null) { 14 XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); 15 //xml文件解析 16 xmlParser.parse(); 17 } 18 } 19 }
xmlParser.parse();是对mapper.xml文件解析。源码分析为
--------------------------------------------------------start xmlParser----------------------------------------------------------------------------------
1 public void parse() { 2 //判断该资源是否已加载 3 if (!configuration.isResourceLoaded(resource)) { 4 //加载mapper.xml文件下的mapper节点 5 configurationElement(parser.evalNode("/mapper")); 6 //加载配置 7 configuration.addLoadedResource(resource); 8 //mapper绑定命名空间 9 bindMapperForNamespace(); 10 } 11 //解析待定的ResultMaps 12 parsePendingResultMaps(); 13 //解析待定的缓存 14 parsePendingCacheRefs(); 15 //解析待定的Statements 16 parsePendingStatements(); 17 }
configurationElement(parser.evalNode("/mapper"));源码为
1 private void configurationElement(XNode context) { 2 try { 3 String namespace = context.getStringAttribute("namespace"); 4 if (namespace == null || namespace.equals("")) { 5 throw new BuilderException("Mapper's namespace cannot be empty"); 6 } 7 //设置命名空间 8 builderAssistant.setCurrentNamespace(namespace); 9 //引用其他namespace的二级缓存 10 cacheRefElement(context.evalNode("cache-ref")); 11 //引用当前namespace的二级缓存 12 cacheElement(context.evalNode("cache")); 13 //parameterMap节点解析
parameterMapElement(context.evalNodes("/mapper/parameterMap")); 14 //resultMap节点解析 15 resultMapElements(context.evalNodes("/mapper/resultMap")); 16 //sql片段节点解析 17 sqlElement(context.evalNodes("/mapper/sql")); 18 //构建statement 19 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); 20 } catch (Exception e) { 21 throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); 22 } 23 }
1 /** 2 *主要将parameterMap节点下的各参数值获取,得到 3 *ParameterMapping对象,再放到parameterMappings列表中 4 */ 5 private void parameterMapElement(List<XNode> list) throws Exception { 6 for (XNode parameterMapNode : list) { 7 String id = parameterMapNode.getStringAttribute("id"); 8 String type = parameterMapNode.getStringAttribute("type"); 9 Class<?> parameterClass = resolveClass(type); 10 List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter"); 11 List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>(); 12 for (XNode parameterNode : parameterNodes) { 13 String property = parameterNode.getStringAttribute("property"); 14 String javaType = parameterNode.getStringAttribute("javaType"); 15 String jdbcType = parameterNode.getStringAttribute("jdbcType"); 16 String resultMap = parameterNode.getStringAttribute("resultMap"); 17 String mode = parameterNode.getStringAttribute("mode"); 18 String typeHandler = parameterNode.getStringAttribute("typeHandler"); 19 Integer numericScale = parameterNode.getIntAttribute("numericScale"); 20 ParameterMode modeEnum = resolveParameterMode(mode); 21 Class<?> javaTypeClass = resolveClass(javaType); 22 JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); 23 @SuppressWarnings("unchecked") 24 Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler); 25 ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale); 26 parameterMappings.add(parameterMapping); 27 } 28 builderAssistant.addParameterMap(id, parameterClass, parameterMappings); 29 } 30 }
1 private void resultMapElements(List<XNode> list) throws Exception { 2 for (XNode resultMapNode : list) { 3 try { 4 resultMapElement(resultMapNode); 5 } catch (IncompleteElementException e) { 6 // ignore, it will be retried 7 } 8 } 9 } 10 11 private ResultMap resultMapElement(XNode resultMapNode) throws Exception { 12 return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList()); 13 } 14 15 private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { 16 ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); 17 String id = resultMapNode.getStringAttribute("id", 18 resultMapNode.getValueBasedIdentifier()); 19 String type = resultMapNode.getStringAttribute("type", 20 resultMapNode.getStringAttribute("ofType", 21 resultMapNode.getStringAttribute("resultType", 22 resultMapNode.getStringAttribute("javaType")))); 23 String extend = resultMapNode.getStringAttribute("extends"); 24 Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); 25 Class<?> typeClass = resolveClass(type); 26 Discriminator discriminator = null; 27 List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); 28 resultMappings.addAll(additionalResultMappings); 29 List<XNode> resultChildren = resultMapNode.getChildren(); 30 for (XNode resultChild : resultChildren) { 31 if ("constructor".equals(resultChild.getName())) { 32 processConstructorElement(resultChild, typeClass, resultMappings); 33 } else if ("discriminator".equals(resultChild.getName())) { 34 discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); 35 } else { 36 List<ResultFlag> flags = new ArrayList<ResultFlag>(); 37 if ("id".equals(resultChild.getName())) { 38 flags.add(ResultFlag.ID); 39 } 40 resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); 41 } 42 } 43 ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); 44 try { 45 return resultMapResolver.resolve(); 46 } catch (IncompleteElementException e) { 47 configuration.addIncompleteResultMap(resultMapResolver); 48 throw e; 49 } 50 }
1 private void sqlElement(List<XNode> list) throws Exception { 2 if (configuration.getDatabaseId() != null) { 3 sqlElement(list, configuration.getDatabaseId()); 4 } 5 sqlElement(list, null); 6 } 7 8 private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception { 9 for (XNode context : list) { 10 String databaseId = context.getStringAttribute("databaseId"); 11 String id = context.getStringAttribute("id"); 12 id = builderAssistant.applyCurrentNamespace(id, false); 13 if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { 14 sqlFragments.put(id, context); 15 } 16 } 17 }
1 public void parseStatementNode() { 2 String id = context.getStringAttribute("id"); 3 String databaseId = context.getStringAttribute("databaseId"); 4 5 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { 6 return; 7 } 8 //指的是一次获取结果数量大小 9 Integer fetchSize = context.getIntAttribute("fetchSize"); 10 Integer timeout = context.getIntAttribute("timeout"); 11 String parameterMap = context.getStringAttribute("parameterMap"); 12 String parameterType = context.getStringAttribute("parameterType"); 13 Class<?> parameterTypeClass = resolveClass(parameterType); 14 String resultMap = context.getStringAttribute("resultMap"); 15 String resultType = context.getStringAttribute("resultType"); 16 String lang = context.getStringAttribute("lang"); 17 LanguageDriver langDriver = getLanguageDriver(lang); 18 Class<?> resultTypeClass = resolveClass(resultType); 19 String resultSetType = context.getStringAttribute("resultSetType"); 20 //判断标签中是否指定了statementType参数,如果没有默认为预编译,主要有三种 STATEMENT, PREPARED, CALLABLE选择 21 StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); 22 ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); 23 //获取节点名称 24 String nodeName = context.getNode().getNodeName(); 25 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); 26 boolean isSelect = sqlCommandType == SqlCommandType.SELECT; 27 boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); 28 boolean useCache = context.getBooleanAttribute("useCache", isSelect); 29 boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); 30 31 // Include Fragments before parsing 先解析sql片段 32 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); 33 includeParser.applyIncludes(context.getNode()); 34 35 // Parse selectKey after includes and remove them. 36 processSelectKeyNodes(id, parameterTypeClass, langDriver); 37 38 // Parse the SQL (pre: <selectKey> and <include> were parsed and removed),创建sql资源(包含了sql语句,参数,配置) 39 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); 40 String resultSets = context.getStringAttribute("resultSets"); 41 String keyProperty = context.getStringAttribute("keyProperty"); 42 String keyColumn = context.getStringAttribute("keyColumn"); 43 KeyGenerator keyGenerator; 44 //statementId 类型为“com.ssc.demo.mybatis.dao.SubjectDao.selectSubjects!selectKey” 45 String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; 46 keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); 47 if (configuration.hasKeyGenerator(keyStatementId)) { 48 keyGenerator = configuration.getKeyGenerator(keyStatementId); 49 } else { 50 keyGenerator = context.getBooleanAttribute("useGeneratedKeys", 51 configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) 52 ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; 53 } 54 55 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, 56 fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, 57 resultSetTypeEnum, flushCache, useCache, resultOrdered, 58 keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); 59 }
--------------------------------------------------------------------------------------------------end ---------------------------------------------------------------------------------------------------------------------------------------------