开局一张图,后面全靠编。Mybatis结构图:
在这张图里面,我们看到configuration在整个Mybatis里面到处都有,跟黄金一样成为必不可少的元素。
这张是整个流程结构的图,从网上找的。
入口在SqlSessionFactoryBuilder#build方法:
//代码从这里开始执行 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //在这里创建xml建造器,并解析xml XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //解析并构建 并使用DefaultSqlSessionFactory对Configuration类 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
先进入XMLConfigBuilder构造里面,我们看到super(new Configuration());这里就是将jdbc jndi这些类注册到typeAliasRegistry中。然后在进入parse方法中,开始解析xml
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { //将jdbc jndi这些注册到typeAliasRegistry super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } //解析xml里面的信息 public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //解析configuration里面的xml parseConfiguration(parser.evalNode("/configuration")); return configuration; }
private void parseConfiguration(XNode root) { try {
//这里讲一下 environments、typeAliases、mappers//issue #117 read properties first propertiesElement(root.evalNode( "properties")) ; Properties settings = settingsAsProperties(root.evalNode( "settings")) ; loadCustomVfs(settings) ; typeAliasesElement(root.evalNode( "typeAliases")) ; 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")) ; } catch (Exception e) { throw new BuilderException( "Error parsing SQL Mapper Configuration. Cause: " + e , e) ; }} typeAliases
private void typeAliasesElement(XNode parent) { if (parent != null) { // 遍历<typeAliases>下的所有子节点 for (XNode child : parent.getChildren()) { // 若当前结点为<package> if ("package".equals(child.getName())) { // 获取<package>上的name属性(包名) String typeAliasPackage = child.getStringAttribute("name"); // 为该包下的所有类起个别名,并注册进configuration的typeAliasRegistry中 configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } // 如果当前结点为< typeAlias > else { // 获取alias和type属性 String alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); // 注册进configuration的typeAliasRegistry中 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); } } } } }
environments
//获取环境 private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } //拿到环境的标识 for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); //如果environment和id都为空时,不解析 直接跳出 if (isSpecifiedEnvironment(id)) { //根据传入的type来创建不同的事务工厂 "jdbc" -> "class org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory" TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //根据传入的type来创建不同的数据源工厂 "pooled" -> "org.apache.ibatis.datasource.pooled.PooledDataSourceFactory" DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); //获得连接池 DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); //将事务工厂、连接池添加到环境中 configuration.setEnvironment(environmentBuilder.build()); } } } }
private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { //获取 <transactionManager type="JDBC"/> String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); //从里面获取到对应的类 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }
public <T> Class<T> resolveAlias(String string) { try { if (string == null) { return null; } // 转换为小写 String key = string.toLowerCase(Locale.ENGLISH); Class<T> value; if (TYPE_ALIASES.containsKey(key)) { //从缓存从拿 value = (Class<T>) TYPE_ALIASES.get(key); } else { value = (Class<T>) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } }
databaseIdProviderElement跟上面的流程一样,在这里就不重复了。处理完成后,把事务、连接池都加入configuration.setEnvironment(environmentBuilder.build());中。
mapper
private void mapperElement(XNode parent) throws Exception { if (parent != null) { // 遍历<mappers>下所有子节点 for (XNode child : parent.getChildren()) { // 如果当前节点为<package> if ("package".equals(child.getName())) { // 获取<package>的name属性(该属性值为mapper class所在的包名) String mapperPackage = child.getStringAttribute("name"); // 将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中 configuration.addMappers(mapperPackage); } // 如果当前节点为<mapper> else { // 依次获取resource、url、class属性 String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // 解析resource属性(Mapper.xml文件的路径) if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); // 将Mapper.xml文件解析成输入流 InputStream inputStream = Resources.getResourceAsStream(resource); // 使用XMLMapperBuilder解析Mapper.xml,并将Mapper Class注册进configuration对象的mapperRegistry容器中 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } // 解析url属性(Mapper.xml文件的路径) else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } // 解析class属性(Mapper Class的全限定名) else if (resource == null && url == null && mapperClass != null) { // 将Mapper Class的权限定名转化成Class对象 Class<?> mapperInterface = Resources.classForName(mapperClass); // 注册进configuration对象的mapperRegistry容器中 configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { // 将configuration赋给BaseBuilder super(configuration); // 创建MapperBuilderAssistant对象 this.builderAssistant = new MapperBuilderAssistant(configuration, resource); this.parser = parser; this.sqlFragments = sqlFragments; this.resource = resource; } public void parse() { //若当前的Mapper.xml尚未被解析,则开始解析 // PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了 if (!configuration.isResourceLoaded(resource)) { // 解析<mapper>节点 configurationElement(parser.evalNode("/mapper")); // 将该Mapper.xml添加至configuration的LoadedResource容器中,下回无需再解析 configuration.addLoadedResource(resource); // 将该Mapper.xml对应的Mapper Class注册进configuration的mapperRegistry容器中 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
加载的资源会在缓存中判断是否已经被解析过,如果没有解析过继续执行,否则跳出。
private void configurationElement(XNode context) { try { // 获取<mapper>节点上的namespace属性,该属性必须存在,表示当前映射文件对应的Mapper Class是谁 String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 将namespace属性值赋给builderAssistant builderAssistant.setCurrentNamespace(namespace); // 解析<cache-ref>节点 cacheRefElement(context.evalNode("cache-ref")); // 解析<cache>节点 cacheElement(context.evalNode("cache")); // 解析<parameterMap>节点 parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析<resultMap>节点 resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析<sql>节点 sqlElement(context.evalNodes("/mapper/sql")); // 解析sql语句 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); } }
这里只讲一下resultMapElements、buildStatementFromContext,其他的可以进行查看
resultMapElements
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); // 获取<ResultMap>上的id属性 String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); // 获取<ResultMap>上的type属性(即resultMap的返回值类型) String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); // 获取extends属性 String extend = resultMapNode.getStringAttribute("extends"); // 获取autoMapping属性 Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 将resultMap的返回值类型转换成Class对象 Class<?> typeClass = resolveClass(type); Discriminator discriminator = null; // resultMappings用于存储<resultMap>下所有的子节点 List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); resultMappings.addAll(additionalResultMappings); // 获取并遍历<resultMap>下所有的子节点 List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { // 若当前节点为<constructor>,则将它的子节点们添加到resultMappings中去 if ("constructor".equals(resultChild.getName())) { processConstructorElement(resultChild, typeClass, resultMappings); } // 若当前节点为<discriminator>,则进行条件判断,并将命中的子节点添加到resultMappings中去 else if ("discriminator".equals(resultChild.getName())) { discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } // 若当前节点为<result>、<association>、<collection>,则将其添加到resultMappings中去 else { // PS:flags仅用于区分当前节点是否是<id>或<idArg>,因为这两个节点的属性名为name,而其他节点的属性名为property List<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } // ResultMapResolver的作用是生成ResultMap对象,并将其加入到Configuration对象的resultMaps容器中(具体过程见下) ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } }
buildStatementFromContext
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { //开始处理 for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { //解析处理 statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
public void parseStatementNode() { //获取id <select id="xxxxxxx"> String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } //解析<select resultType="com.telecom.Blog" id="selectBlog"> 这里的很多标签 Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); //获得是哪种操作 select update insert delete String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; //创建key selectBlog!selectKey String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; //com.telecom.BlogMapper.selectBlog!selectKey keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); //判断是否存在这个key缓存 if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { //解析useGeneratedKeys keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { if (requiredDatabaseId != null) { if (!requiredDatabaseId.equals(databaseId)) { return false; } } else { if (databaseId != null) { return false; } // skip this statement if there is a previous one with a not null databaseId //获取com.telecom.BlogMapper.selectBlog 判断是否已经存在缓存中 id = builderAssistant.applyCurrentNamespace(id, false); if (this.configuration.hasStatement(id, false)) { MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2 if (previous.getDatabaseId() != null) { return false; } } } return true; }
然后回到XMLMapperBuilder#parse
public void parse() { //若当前的Mapper.xml尚未被解析,则开始解析 // PS:若<mappers>节点下有相同的<mapper>节点,那么就无需再次解析了 if (!configuration.isResourceLoaded(resource)) { // 解析<mapper>节点 configurationElement(parser.evalNode("/mapper")); // 将该Mapper.xml添加至configuration的LoadedResource容器中,下回无需再解析 configuration.addLoadedResource(resource); // 将该Mapper.xml对应的Mapper Class注册进configuration的mapperRegistry容器中 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
private void bindMapperForNamespace() { //获得当前空间 com.telecom.BlogMapper String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { //得到类信息 interface com.telecom.BlogMapper boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { //从MapperRegistry.hasMapper里面判断是否存在 if (!configuration.hasMapper(boundType)) { //把namespace:com.telecom.BlogMapper 放入到加载的资源 configuration.addLoadedResource("namespace:" + namespace); //把类型放入map缓存中 configuration.addMapper(boundType); } } } }
在回退到SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
//代码从这里开始执行 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //在这里创建xml建造器,并解析xml XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //解析并构建 并使用DefaultSqlSessionFactory对Configuration类 return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
包装一层DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
这就完成了整个xml的解析,这些都是都在config中。所以configuration这个东西在Mybatis简直无处不在。
上一篇: Mybatis源码分析准备
下一篇: Mybatis源码动态代理调用