mybatis 源码分析之sqlSessionFactory的创建过程
- sqlSessionFactory是什么?
- 一、通过SqlSessionFactoryBuilder的build方法创建SqlSessionFactory源码详解
- buid方法--使用构建者模式
- parser.parse()源码--生成Configuration 对象
- parseConfiguration()源码
- 解析properties标签
- 解析 settings 标签
- 将setting标签中的name和value解析到properties中
- 解析 typeAliases 标签,将配置类注册到TypeAliasRegistry中。给类取的别名
- 解析 mappers 标签--也就是我们写的具体表的xml文件
- 根据配置的mapper所在的包进行解析addMappers
- 解析待定的 resultMap 节点
- 解析待定的 cache-ref 节点
- 解析待定的 SQL语句的节点
- 总结
sqlSessionFactory是什么?
构建者模式:使用多个简单的对象一步一步构建 成一个复杂的对象
代码使用例子
// 1. 读取配置文件,读成字节输入流,注意:现在还没解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. 解析配置文件,封装Configuration对象 创建DefaultSqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 3. 生产了DefaultSqlsession实例对象 设置了事务不自动提交 完成了executor对象的创建
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.(1)根据statementid来从Configuration中map集合中获取到了指定的MappedStatement对象
//(2)将查询任务委派了executor执行器
User user = sqlSession.selectOne("com.share.mapper.IUserMapper.findById",1);
System.out.println(user);
// 5.释放资源
sqlSession.close();
一、通过SqlSessionFactoryBuilder的build方法创建SqlSessionFactory源码详解
buid方法–使用构建者模式
构建者模式:使用多个简单的对象一步一步构建 成一个复杂的对象
// 1.我们最初调用的build
public SqlSessionFactory build(InputStream inputStream) {
//调用了重载方法
return build(inputStream, null, null);
}
// 2.调用的重载方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类
// 里面会生成XPathParser 解析对象 创建 Document 对象 对XML进行验证
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 执行 XML 解析
// 创建 DefaultSqlSessionFactory 对象
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.
}
}
}
parser.parse()源码–生成Configuration 对象
/**
* 解析 XML 成 Configuration 对象。
*
* @return Configuration 对象
*/
public Configuration parse() {
// 若已解析,抛出 BuilderException 异常
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
// 标记已解析
parsed = true;
///parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签
// 解析 XML configuration 节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration()源码
/**
* 解析 XML
*
* 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
*
* @param root 根节点
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析 <properties /> 标签
propertiesElement(root.evalNode("properties"));
// 解析 <settings /> 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 加载自定义的 VFS 实现类
loadCustomVfs(settings);
// 解析 <typeAliases /> 标签
typeAliasesElement(root.evalNode("typeAliases"));
// 解析 <plugins /> 标签
pluginElement(root.evalNode("plugins"));
// 解析 <objectFactory /> 标签
objectFactoryElement(root.evalNode("objectFactory"));
// 解析 <objectWrapperFactory /> 标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 <reflectorFactory /> 标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 赋值 <settings /> 到 Configuration 属性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析 <environments /> 标签
environmentsElement(root.evalNode("environments"));
// 解析 <databaseIdProvider /> 标签
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析 <typeHandlers /> 标签
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析 <mappers /> 标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解析properties标签
/**
* 1. 解析 <properties /> 标签,成 Properties 对象。
* 2. 覆盖 configuration 中的 Properties 对象到上面的结果。
* 3. 设置结果到 parser 和 configuration 中
*
* @param context 节点
* @throws Exception 解析发生异常
*/
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 读取子标签们,为 Properties 对象
Properties defaults = context.getChildrenAsProperties();
// 读取 resource 和 url 属性
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) { // resource 和 url 都存在的情况下,抛出 BuilderException 异常
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
// 读取本地 Properties 配置文件到 defaults 中。
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
// 读取远程 Properties 配置文件到 defaults 中。
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 覆盖 configuration 中的 Properties 对象到 defaults 中。
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 设置 defaults 到 parser 和 configuration 中。
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
解析 settings 标签
/**
* 将 <setting /> 标签解析为 Properties 对象
*
* @param context 节点
* @return Properties 对象
*/
private Properties settingsAsProperties(XNode context) {
// 将子标签,解析成 Properties 对象
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
// 校验每个属性,在 Configuration 中,有相应的 setting 方法,否则抛出 BuilderException 异常
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
将setting标签中的name和value解析到properties中
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
解析 typeAliases 标签,将配置类注册到TypeAliasRegistry中。给类取的别名
/**
* 解析 <typeAliases /> 标签,将配置类注册到 {@link org.apache.ibatis.type.TypeAliasRegistry} 中。
*
* @param parent 节点
*/
private void typeAliasesElement(XNode parent) {
if (parent != null) {
// 遍历子节点
for (XNode child : parent.getChildren()) {
// 指定为包的情况下,注册包下的每个类
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
// 指定为类的情况下,直接注册类和别名
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type); // 获得类是否存在
// 注册到 typeAliasRegistry 中
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) { // 若类不存在,则抛出 BuilderException 异常
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
解析 mappers 标签–也就是我们写的具体表的xml文件
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 遍历子节点
for (XNode child : parent.getChildren()) {
// 如果是 package 标签,则扫描该包
if ("package".equals(child.getName())) {
// 获取 <package> 节点中的 name 属性
String mapperPackage = child.getStringAttribute("name");
// 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置
configuration.addMappers(mapperPackage);
// 如果是 mapper 标签,
} else {
// 获得 resource、url、class 属性
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// resource 不为空,且其他两者为空,则从指定路径中加载配置
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// 获得 resource 的 InputStream 对象
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建 XMLMapperBuilder 对象
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 执行解析
mapperParser.parse();
// url 不为空,且其他两者为空,则通过 url 加载配置
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
// 获得 url 的 InputStream 对象
InputStream inputStream = Resources.getUrlAsStream(url);
// 创建 XMLMapperBuilder 对象
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 执行解析
mapperParser.parse();
// mapperClass 不为空,且其他两者为空,则通过 mapperClass 解析映射配置
} else if (resource == null && url == null && mapperClass != null) {
// 获得 Mapper 接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 添加到 configuration 中
configuration.addMapper(mapperInterface);
// 以上条件不满足,则抛出异常
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
根据配置的mapper所在的包进行解析addMappers
根据配置的dao层中的类解析对应的namespace的xml中的方法与类中方法的关联用于后面的调用
public void addMappers(String packageName) {
// 扫描该包下所有的 Mapper 接口,并添加到 mapperRegistry 中
mapperRegistry.addMappers(packageName);
}
/**
* 扫描指定包,并将符合的类,添加到 {@link #knownMappers} 中
*
* @since 3.2.2
*/
public void addMappers(String packageName, Class<?> superType) {
// 扫描指定包下的指定类
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
// 遍历,添加到 knownMappers 中
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
public <T> void addMapper(Class<T> type) {
// 判断,必须是接口。
if (type.isInterface()) {
// 已经添加过,则抛出 BindingException 异常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 添加到 knownMappers 中
//这个类中维护一个HashMap存放MapperProxyFactory
//private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 解析 Mapper 的注解配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
// 标记加载完成
loadCompleted = true;
} finally {
// 若加载未完成,从 knownMappers 中移除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
/**
* 解析注解
*/
public void parse() {
// 判断当前 Mapper 接口是否应加载过。
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
// 加载对应的 XML Mapper 解析里面的select标签等等并封装到MappedStatement类中
loadXmlResource();
// 标记该 Mapper 接口已经加载过
configuration.addLoadedResource(resource);
// 设置 namespace 属性
assistant.setCurrentNamespace(type.getName());
// 解析 @CacheNamespace 注解
parseCache();
// 解析 @CacheNamespaceRef 注解
parseCacheRef();
// 遍历每个方法,解析其上的注解
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 执行解析
parseStatement(method);
}
} catch (IncompleteElementException e) {
// 解析失败,添加到 configuration 中
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
// 解析待定的方法
parsePendingMethods();
}
/**
* 加载对应的 XML Mapper
*/
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
// 判断 Mapper XML 是否已经加载过,如果加载过,就不加载了。
// 此处,是为了避免和 XMLMapperBuilder#parse() 方法冲突,重复解析
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// 获得 InputStream 对象
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
// 创建 XMLMapperBuilder 对象,执行解析
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
解析待定的 resultMap 节点
// 创建 ResultMap 对象,并添加到 Configuration 中。
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
// 获得 ResultMap 编号,即格式为 `${namespace}.${id}` 。
id = applyCurrentNamespace(id, false);
// 获取完整的 extend 属性,即格式为 `${namespace}.${extend}` 。从这里的逻辑来看,貌似只能自己 namespace 下的 ResultMap 。
extend = applyCurrentNamespace(extend, true);
// 如果有父类,则将父类的 ResultMap 集合,添加到 resultMappings 中。
if (extend != null) {
// 获得 extend 对应的 ResultMap 对象。如果不存在,则抛出 IncompleteElementException 异常
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
ResultMap resultMap = configuration.getResultMap(extend);
// 获取 extend 的 ResultMap 对象的 ResultMapping 集合,并移除 resultMappings
List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
// 判断当前的 resultMappings 是否有构造方法,如果有,则从 extendedResultMappings 移除所有的构造类型的 ResultMapping 们
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
}
// 将 extendedResultMappings 添加到 resultMappings 中
resultMappings.addAll(extendedResultMappings);
}
// 创建 ResultMap 对象
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
// 添加到 configuration 中
configuration.addResultMap(resultMap);
return resultMap;
}
解析待定的 cache-ref 节点
/**
* 获得指向的 Cache 对象
*
* @param namespace 指向的命名空间
* @return Cache 对象
*/
public Cache useCacheRef(String namespace) {
if (namespace == null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
}
try {
unresolvedCacheRef = true; // 标记未解决
// 获得 Cache 对象
Cache cache = configuration.getCache(namespace);
// 获得不到,抛出 IncompleteElementException 异常
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
}
// 记录当前 Cache 对象
currentCache = cache;
unresolvedCacheRef = false; // 标记已解决
return cache;
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
}
}
解析待定的 SQL语句的节点
/**
* 执行解析
*/
public void parseStatementNode() {
// 获得 id 属性,编号。
String id = context.getStringAttribute("id");
// 获得 databaseId , 判断 databaseId 是否匹配
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 获得各种属性
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");
// 获得 lang 对应的 LanguageDriver 对象
LanguageDriver langDriver = getLanguageDriver(lang);
// 获得 resultType 对应的类
Class<?> resultTypeClass = resolveClass(resultType);
// 获得 resultSet 对应的枚举值
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
// 获得 statementType 对应的枚举值
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
// 获得 SQL 对应的 SqlCommandType 枚举值
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 对象,并替换 <include /> 标签相关的内容
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
// 解析 <selectKey /> 标签
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
// 创建 SqlSource 对象
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
// 获得 KeyGenerator 对象
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
// 优先,从 configuration 中获得 KeyGenerator 对象。如果存在,意味着是 <selectKey /> 标签配置的
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
// 其次,根据标签属性的情况,判断是否使用对应的 Jdbc3KeyGenerator 或者 NoKeyGenerator 对象
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys", // 优先,基于 useGeneratedKeys 属性判断
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) // 其次,基于全局的 useGeneratedKeys 配置 + 是否为插入语句类型
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 创建 MappedStatement 对象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
总结
描述了创建SqlSessionFactory时大致的过程,主要是加载配置文件读取成流并封装到configuration中,将xml中的sql语句封装到MappedStatement中,大量使用构建模式和动态代理模式等来创建对象。