Mybatis之Configuration初始化(配置文件.xml的解析)
                        
                     
                    
                    
                        源码解读第一步我觉着应该从Mybatis如何解析配置文件开始。
1.先不看跟Spring集成如何解析,先看从SqlSessionFactoryBuilder如果解析的。
复制代码
1 String resouce = "conf.xml";
2 InputStream is = Resources.getResourceAsStream(resouce);
3 
4 // 构建sqlSession工厂
5 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
复制代码
SqlSessionFactoryBuilder
复制代码
 1   public SqlSessionFactory build(InputStream inputStream) {
 2     return build(inputStream, null, null);
 3   }
 4 
 5   public SqlSessionFactory build(InputStream inputStream, String environment) {
 6     return build(inputStream, environment, null);
 7   }
 8 
 9   public SqlSessionFactory build(InputStream inputStream, Properties properties) {
10     return build(inputStream, null, properties);
11   }
12   //上面那么多同名不同参的方法最后都会进入这个方法
   //支持传入Enviromment.properties实际上是支持动态传入覆盖全局配置xml的内容
13   public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
14     try {
15       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //因为从下面的Build方法可以看出 parser.parse()后Configuration就初始化好了
16       return build(parser.parse());
17     } catch (Exception e) {
18       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
19     } finally {
20       ErrorContext.instance().reset();
21       try {
22         inputStream.close();
23       } catch (IOException e) {
24         // Intentionally ignore. Prefer previous error.
25       }
26     }
27   }
   public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
   }
复制代码
真正初始化Configuration的类是XMLConfigBuilder
复制代码
 1   public Configuration parse() {
    //这一块可以看出来 全局Configuration只会初始化一次,实例化时候是false
 2     if (parsed) {
 3       throw new BuilderException("Each XMLConfigBuilder can only be used once.");
 4     }
 5     parsed = true;
 6     parseConfiguration(parser.evalNode("/configuration"));
 7     return configuration;
 8   }
 9   //获取配置文件整个/configuration节点内容
10   private void parseConfiguration(XNode root) {
11     try {
12       Properties settings = settingsAsPropertiess(root.evalNode("settings")); //初始化配置文件中全局变量
13       //issue #117 read properties first
14       propertiesElement(root.evalNode("properties"));//初始化xml中的配置文件,同时讲其中的变量保存起来,因为可能其他地方引用
15       loadCustomVfs(settings); //加载自定义的VFS实现类? 这个什么用?
16       typeAliasesElement(root.evalNode("typeAliases")); //加载typeAliases别名初始化
17       pluginElement(root.evalNode("plugins")); //加载插件,实际上就是拦截器
18       objectFactoryElement(root.evalNode("objectFactory"));// //加载自定义的对象工厂
19       objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //加载自定义的处理驼峰方式的key的处理器
20       reflectionFactoryElement(root.evalNode("reflectionFactory")); //加载自定义的反射器  没用过?
21       settingsElement(settings); //将setting的属性,设置到Configuration对象属性中
22       // read it after objectFactory and objectWrapperFactory issue #631
23       environmentsElement(root.evalNode("environments"));
24       databaseIdProviderElement(root.evalNode("databaseIdProvider"));
25       typeHandlerElement(root.evalNode("typeHandlers")); //初始化类型处理器
26       mapperElement(root.evalNode("mappers")); //处理mappers节点内容,实际上就是初始化MaperStatement
27     } catch (Exception e) {
28       throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
29     }
30   }
复制代码
因为大部分方法都比较简单,我这里只介绍几个我认为比较重要的。
① typeAliasesElement(root.evalNode("typeAliases")); //加载typeAliases别名初始化
复制代码
 1   private void typeAliasesElement(XNode parent) {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { //配置的是包名 扫描包里所有的类。放入的key默认是注解的key
 5           String typeAliasPackage = child.getStringAttribute("name");
 6           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
 7         } else {
 8           String alias = child.getStringAttribute("alias");
 9           String type = child.getStringAttribute("type");
10           try {
11             Class> clazz = Resources.classForName(type);
12             if (alias == null) {
13               typeAliasRegistry.registerAlias(clazz);
14             } else {
15               typeAliasRegistry.registerAlias(alias, clazz);
16             }
         //实际上就是存在了TypeAliasRegistry类的一个私有map里面。
17           } catch (ClassNotFoundException e) {
18             throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
19           }
20         }
21       }
22     }
23   }
复制代码
复制代码
1 public class TypeAliasRegistry {
2 
3   private final Map> TYPE_ALIASES = new HashMap>();
  。。。。。。
4 }
复制代码
 
② pluginElement(root.evalNode("plugins")); 加载插件,便于后期理解拦截器原理
复制代码
 1   private void pluginElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         String interceptor = child.getStringAttribute("interceptor");
 5         Properties properties = child.getChildrenAsProperties();
       //获取interceptor 这里resolveClass实际上就是到TypeAliasResitstry里面找一下,找到了获取class,没有直接用class去反射回去对象
 6         Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
 7         interceptorInstance.setProperties(properties);
       //获取对象后调用configuration方法。
 8         configuration.addInterceptor(interceptorInstance);
 9       }
10     }
11   }
复制代码
Configuration
复制代码
1  public void addInterceptor(Interceptor interceptor) {
2     interceptorChain.addInterceptor(interceptor);
3   }
复制代码
InterceptorChain
复制代码
1   public void addInterceptor(Interceptor interceptor) {
2     interceptors.add(interceptor);
3   }
复制代码
这一块就是放Configuration的拦截器链里面添加拦截器。这一块现在知道是在这时候添加的就好了。后面介绍Mybatis的拦截器的时候深入了解。
③ mapperElement(root.evalNode("mappers")); //处理mappers节点内容,实际上就是初始化MaperStatement
复制代码
 1   private void mapperElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { 
 5           String mapperPackage = child.getStringAttribute("name");
 6           configuration.addMappers(mapperPackage);
 7         } else {
 8           String resource = child.getStringAttribute("resource");
 9           String url = child.getStringAttribute("url");
10           String mapperClass = child.getStringAttribute("class");
        //xml文件中mapper节点,配置resource,url是执行的mapper.xml文件的位置,所以现在只分析这一块。
11           if (resource != null && url == null && mapperClass == null) { 
12             ErrorContext.instance().resource(resource);
13             InputStream inputStream = Resources.getResourceAsStream(resource);
14             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
15             mapperParser.parse();
16           } else if (resource == null && url != null && mapperClass == null) {
17             ErrorContext.instance().resource(url);
18             InputStream inputStream = Resources.getUrlAsStream(url);
19             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
20             mapperParser.parse();
21           } else if (resource == null && url == null && mapperClass != null) {
22             Class> mapperInterface = Resources.classForName(mapperClass);
23             configuration.addMapper(mapperInterface);
24           } else {
25             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
26           }
27         }
28       }
29     }
30   }
复制代码
可以看出来resource和url都是通过XMLMapperBuilder来解析的。下面来看下parse方法。
复制代码
 1   private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map sqlFragments) {
 2     super(configuration);
       //前面还有个构造器,是根据传入的inputstream构造XPathParser,parser可以拿到resouce里面的内容
 3     this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
 4     this.parser = parser;
 5     this.sqlFragments = sqlFragments;
 6     this.resource = resource;
 7   }
 8 
 9   public void parse() {
10     if (!configuration.isResourceLoaded(resource)) { 是否加载过改文件,
11       configurationElement(parser.evalNode("/mapper")); 来解析mapper文件内容
12       configuration.addLoadedResource(resource); 添加加载记录
13       bindMapperForNamespace();往configration添加命名空间的代理对象
14     }
15 
16     parsePendingResultMaps();
17     parsePendingChacheRefs();
18     parsePendingStatements();
19   }
复制代码
复制代码
 1   private void configurationElement(XNode context) { //解析mapper.xml子节点的内容
 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       builderAssistant.setCurrentNamespace(namespace);
 8       cacheRefElement(context.evalNode("cache-ref"));
 9       cacheElement(context.evalNode("cache"));
10       parameterMapElement(context.evalNodes("/mapper/parameterMap"));
11       resultMapElements(context.evalNodes("/mapper/resultMap")); //解析resultMap 很复杂,后面单独解读
12       sqlElement(context.evalNodes("/mapper/sql"));
13       buildStatementFromContext(context.evalNodes("select|insert|update|delete")); //初始化这几个类型节点的内容
14     } catch (Exception e) {
15       throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
16     }
17   }
复制代码
复制代码
 1   private void buildStatementFromContext(List list) {
       因为会有多个节点,所以是一个List
 2     if (configuration.getDatabaseId() != null) {
 3       buildStatementFromContext(list, configuration.getDatabaseId());
 4     }
 5     buildStatementFromContext(list, null);
 6   }
 7 
 8   private void buildStatementFromContext(List list, String requiredDatabaseId) {
 9     for (XNode context : list) {
10       final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
11       try {
12         statementParser.parseStatementNode();
13       } catch (IncompleteElementException e) {
14         configuration.addIncompleteStatement(statementParser);
15       }
16     }
17   }
复制代码
从上面可以看出来最终是由XMLStatementBuilder来解析我们的写的sql部分。
复制代码
 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 
19     Class> resultTypeClass = resolveClass(resultType);
20     String resultSetType = context.getStringAttribute("resultSetType");
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
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     //根据前面的语言驱动去获取对应的SqlSource。SqlSource有两种,一种是处理${}一种是处理#{}
38     // Parse the SQL (pre:  and  were parsed and removed)
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     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
45     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
46     if (configuration.hasKeyGenerator(keyStatementId)) {
47       keyGenerator = configuration.getKeyGenerator(keyStatementId);
48     } else {
49       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
50           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
51           ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
52     }
53   获取节点所有属性内容,调用builderAssistant,实现是MapperBuilderAssistant 在XmlMapperBuilder构造器初始化时候就制定了
54     builderAssistant.addMappedS