mybatis源码阅读-初始化过程(七)
说明
mybatis初始化过程 就是解析xml到封装成Configuration对象 供后续使用
SqlSessionFactoryBuilder
代码例子
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder .build(ClassLoader.getSystemResourceAsStream("mybatis.xml"));
说明
通过build将流交给XMLConfigBuilder处理 XMLConfigBuilder通过parse解析XML封装到Configuration 然后SqlSessionFactoryBuilder 创建DefaultSqlSessionFactory 并将解析的Configuration 设置到DefaultSqlSessionFactory对象的属性
源码
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { //将流传入XMLConfigBuilder XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //parser.parse() 解析xml和mapper文件 封装成Configuration并返回 var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException var13) { ; } } return var5; } public SqlSessionFactory build(Configuration config) { //初始化Configuration return new DefaultSqlSessionFactory(config); }
XMLConfigBuilder
说明
负责解析指定xml并封装成Configuration 对象
源码
public Configuration parse() { if (this.parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } else { this.parsed = true; //获得根节点configuration this.parseConfiguration(this.parser.evalNode("/configuration")); return this.configuration; } } private void parseConfiguration(XNode root) { try { //解析settings并封装成Properties Properties settings = this.settingsAsPropertiess(root.evalNode("settings")); //解析properties this.propertiesElement(root.evalNode("properties")); this.loadCustomVfs(settings); //解析typeAliases this.typeAliasesElement(root.evalNode("typeAliases")); //解析plugins this.pluginElement(root.evalNode("plugins")); //解析objectFactory this.objectFactoryElement(root.evalNode("objectFactory")); this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectionFactoryElement(root.evalNode("reflectionFactory")); this.settingsElement(settings); this.environmentsElement(root.evalNode("environments")); this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); this.typeHandlerElement(root.evalNode("typeHandlers")); this.mapperElement(root.evalNode("mappers")); } catch (Exception var3) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); } }
注意root.evalNode为mybatis封装的解析xml的工具类 root为XNode对象 感兴趣可以查看
mapper解析过程
mapperElement(root.evalNode("mappers"))
private void mapperElement(XNode parent) throws Exception { if (parent != null) { Iterator i$ = parent.getChildren().iterator(); while(true) { while(i$.hasNext()) { XNode child = (XNode)i$.next(); String resource; //2种配置选择 <mappers><mapper resource="ClassesMapper.xml"></mapper><mapper class=""></mapper><package name="com.liqiang.mapper"/></mappers> if ("package".equals(child.getName())) { resource = child.getStringAttribute("name"); this.configuration.addMappers(resource); } else { resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); XMLMapperBuilder mapperParser; InputStream inputStream; if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); inputStream = Resources.getResourceAsStream(resource); //得到对应的mapper.xml文件 交给XmlMapperBuilder解析 并将结果封装到configuration mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); inputStream = Resources.getUrlAsStream(url); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments()); mapperParser.parse(); } else { if (resource != null || url != null || mapperClass == null) { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } Class<?> mapperInterface = Resources.classForName(mapperClass); this.configuration.addMapper(mapperInterface); } } } return; } } }
XMLMapperBuilder
解析源码
private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); if (namespace != null && !namespace.equals("")) { this.builderAssistant.setCurrentNamespace(namespace); this.cacheRefElement(context.evalNode("cache-ref")); this.cacheElement(context.evalNode("cache")); this.parameterMapElement(context.evalNodes("/mapper/parameterMap")); this.resultMapElements(context.evalNodes("/mapper/resultMap")); this.sqlElement(context.evalNodes("/mapper/sql")); this.buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } else { throw new BuilderException("Mapper's namespace cannot be empty"); } } catch (Exception var3) { throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3); } }
图解
图片来源:https://my.oschina.net/zudajun/blog/668738
最终这些标签都会以对象的形式封装起来以map格式保存到configuration 的map里面key为id如:
public class Configuration { protected final Map<String, MappedStatement> mappedStatements; protected final Map<String, Cache> caches; protected final Map<String, ResultMap> resultMaps; protected final Map<String, ParameterMap> parameterMaps; protected final Map<String, KeyGenerator> keyGenerators; }
其他
typeAlias package扫描
<typeAliases>
<typeAlias alias="Student" type="com.mybatis3.domain.Student" />
<typeAlias alias="Teacher" type="com.mybatis3.domain.Teacher" />
<package name="com.mybatis3.domain" />
</typeAliases>
XMLConfigBuilder
private void typeAliasesElement(XNode parent) { if (parent != null) { Iterator i$ = parent.getChildren().iterator(); while(i$.hasNext()) { XNode child = (XNode)i$.next(); String alias; //如果是package反射获得类下面的所有类型 registerAlias if ("package".equals(child.getName())) { alias = child.getStringAttribute("name"); this.configuration.getTypeAliasRegistry().registerAliases(alias); } else { alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); try { Class<?> clazz = Resources.classForName(type); if (alias == null) { //如果没有指定别名 内部会读取类上面的Alias注解的value为别名 this.typeAliasRegistry.registerAlias(clazz); } else { //注册 this.typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException var7) { throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + var7, var7); } } } } }
简写和全名称都能执行sql原理
表现形式
Student std = sqlSession.selectOne("findStudentById", 1);
Student std = sqlSession.selectOne("com.mybatis3.mappers.StudentMapper.findStudentById", 1);
原理
mybatis重写保存Statement的Configuration.StrictMap
this.mappedStatements = new Configuration.StrictMap("Mapped Statements collection"); this.caches = new Configuration.StrictMap("Caches collection"); this.resultMaps = new Configuration.StrictMap("Result Maps collection"); this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection"); this.keyGenerators = new Configuration.StrictMap("Key Generators collection");】
public V put(String key, V value) { if (this.containsKey(key)) { throw new IllegalArgumentException(this.name + " already contains value for " + key); } else { //判断是否是配的全名称 if (key.contains(".")) { //获得简写形式 String shortKey = this.getShortName(key); //判断是否存在 if (super.get(shortKey) == null) { //如果不存在直接放进去 super.put(shortKey, value); } else { //如果重复 则保存一个占位符 get的时候判断值是这个占位符 则抛错 super.put(shortKey, new Configuration.StrictMap.Ambiguity(shortKey)); } } //原始配置保存 return super.put(key, value); } } public V get(Object key) { V value = super.get(key); if (value == null) { throw new IllegalArgumentException(this.name + " does not contain value for " + key); //如果是Ambiguity 则抛错 } else if (value instanceof Configuration.StrictMap.Ambiguity) { throw new IllegalArgumentException(((Configuration.StrictMap.Ambiguity)value).getSubject() + " is ambiguous in " + this.name + " (try using the full name including the namespace, or rename one of the entries)"); } else { return value; } } }