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
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信