MyBatis源码分析之——配置解析创建SqlSessionFactory的过程

大家应该都知道Mybatis源码也是对Jbdc的再一次封装,不管怎么进行包装,还是会有获取链接、
preparedStatement、封装参数、执行这些步骤的。

配置解析过程

String resource = "mybatis-config.xml";
//1.读取resources下面的mybatis-config.xml文件
InputStream inputStream = Resources.getResourceAsStream(resource);
//2.使用SqlSessionFactoryBuilder创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.通过sqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

1.Resources.getResourceAsStream(resource)读取文件

public static InputStream getResourceAsStream(String resource) throws IOException {
	return getResourceAsStream(null, resource);
} 
//loader赋值为null
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
	InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
	if (in == null) {
		throw new IOException("Could not find resource " + resource);
	} 
	return in;
}
//classLoader为null
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
	return getResourceAsStream(resource, getClassLoaders(classLoader));
} 
//classLoader类加载
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
	for (ClassLoader cl : classLoader) {
		if (null != cl) {
			//加载指定路径文件流
			InputStream returnValue = cl.getResourceAsStream(resource);
			// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
			if (null == returnValue) {
				returnValue = cl.getResourceAsStream("/" + resource);
			} 
			if (null != returnValue) {
				return returnValue;
			}
		}
	} 
	return null;
}

总结:主要是通过ClassLoader.getResourceAsStream()方法获取指定的classpath路径下的Resource

2.通过SqlSessionFactoryBuilder创建SqlSessionFactory

//SqlSessionFactoryBuilder是一个建造者模式
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
public SqlSessionFactory build(InputStream inputStream) {
	return build(inputStream, null, null);
}
//XMLConfigBuilder也是建造者模式
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
	try {
		XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
		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构造函数
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
	this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()),
	environment, props);
}
//接下来进入this后,初始化Configuration
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	super(new Configuration());
	ErrorContext.instance().resource("SQL Mapper Configuration");
	this.configuration.setVariables(props);
	this.parsed = false;
	this.environment = environment;
	this.parser = parser;
}
//其中parser.parse()负责解析xml,build(configuration)创建SqlSessionFactory
return build(parser.parse())

(1)parser.parse()解析xml

public Configuration parse() {
	//判断是否重复解析
	if (parsed) {
		throw new BuilderException("Each XMLConfigBuilder can only be used once.");
	} 
	parsed = true;
	//读取配置文件一级节点configuration
	parseConfiguration(parser.evalNode("/configuration"));
	return configuration;
}
private void parseConfiguration(XNode root) {
	try {
		//properties 标签,用来配置参数信息,比如最常见的数据库连接信息
		propertiesElement(root.evalNode("properties"));
		Properties settings = settingsAsProperties(root.evalNode("settings"));
		loadCustomVfs(settings);
		loadCustomLogImpl(settings);
		//实体别名两种方式:1.指定单个实体;2.指定包
		typeAliasesElement(root.evalNode("typeAliases"));
		//插件
		pluginElement(root.evalNode("plugins"));
		//用来创建对象(数据库数据映射成java对象时)
		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"));
		//数据库类型和Java数据类型的转换
		typeHandlerElement(root.evalNode("typeHandlers"));
		//这个是对数据库增删改查的解析
		mapperElement(root.evalNode("mappers"));
	} catch (Exception e) {
		throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
	}
}

总结:parseConfiguration完成的是解析configuration下的标签

private void mapperElement(XNode parent) throws Exception {
	if (parent != null) {
		for (XNode child : parent.getChildren()) {
			//解析
			if ("package".equals(child.getName())) {
				String mapperPackage = child.getStringAttribute("name");
				//包路径存到mapperRegistry中
				configuration.addMappers(mapperPackage);
			} else {
				//解析
				String resource = child.getStringAttribute("resource");
				String url = child.getStringAttribute("url");
				String mapperClass = child.getStringAttribute("class");
				if (resource != null && url == null && mapperClass == null) {
					ErrorContext.instance().resource(resource);
					//读取Mapper.xml文件
					InputStream inputStream = Resources.getResourceAsStream(resource);
					XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream,
					configuration, resource, configuration.getSqlFragments());
					mapperParser.parse();
				} 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();
				} else if (resource == null && url == null && mapperClass != null) {
					Class mapperInterface = Resources.classForName(mapperClass);
					configuration.addMapper(mapperInterface);
				} else {
					throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
				}
			}
		}
	}
}

总结: 通过解析configuration.xml文件,获取其中的Environment、Setting,重要的是将下的所有解析出来之后添加到
Configuration,Configuration类似于配置中心,所有的配置信息都在这里。

(2)mapperParser.parse()对 Mapper 映射器的解析

public void parse() {
	if (!configuration.isResourceLoaded(resource)) {
		//解析所有的子标签
		configurationElement(parser.evalNode("/mapper"));
		configuration.addLoadedResource(resource);
		//把namespace(接口类型)和工厂类绑定起来
		bindMapperForNamespace();
	}
	parsePendingResultMaps();
	parsePendingCacheRefs();
	parsePendingStatements();
} 
//这里面解析的是Mapper.xml的标签
private void configurationElement(XNode context) {
	try {
		String namespace = context.getStringAttribute("namespace");
		if (namespace == null || namespace.equals("")) {
			throw new BuilderException("Mapper's namespace cannot be empty");
		} 
		builderAssistant.setCurrentNamespace(namespace);
		//对其他命名空间缓存配置的引用
		cacheRefElement(context.evalNode("cache-ref"));
		//对给定命名空间的缓存配置
		cacheElement(context.evalNode("cache"));
		parameterMapElement(context.evalNodes("/mapper/parameterMap"));
		//是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象
		resultMapElements(context.evalNodes("/mapper/resultMap"));
		//可被其他语句引用的可重用语句块
		sqlElement(context.evalNodes("/mapper/sql"));
		//获得MappedStatement对象(增删改查标签)
		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);
	}
}
//获得MappedStatement对象(增删改查标签)
private void buildStatementFromContext(List list) {
	if (configuration.getDatabaseId() != null) {
		buildStatementFromContext(list, configuration.getDatabaseId());
	} 
	buildStatementFromContext(list, null);
}
//获得MappedStatement对象(增删改查标签)
private void buildStatementFromContext(List list, String requiredDatabaseId) {
	//循环增删改查标签
	for (XNode context : list) {
		final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration,
		builderAssistant, context, requiredDatabaseId);
		try {
			//解析insert/update/select/delete中的标签
			statementParser.parseStatementNode();
		} catch (IncompleteElementException e) {
			configuration.addIncompleteStatement(statementParser);
		}
	}
}
public void parseStatementNode() {
	//在命名空间中唯一的标识符,可以被用来引用这条语句
	String id = context.getStringAttribute("id");
	//数据库厂商标识
	String databaseId = context.getStringAttribute("databaseId");
	if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
		return;
	} 
	String nodeName = context.getNode().getNodeName();
	SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
	boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
	//flushCache和useCache都和二级缓存有关
	//将其设置为true后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false
	boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
	//将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true
	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());
	//会传入这条语句的参数类的完全限定名或别名
	String parameterType = context.getStringAttribute("parameterType");
	Class parameterTypeClass = resolveClass(parameterType);
	String lang = context.getStringAttribute("lang");
	LanguageDriver langDriver = getLanguageDriver(lang);
	// Parse selectKey after includes and remove them.
	processSelectKeyNodes(id, parameterTypeClass, langDriver);
	// Parse the SQL (pre:  and  were parsed and removed)
	KeyGenerator keyGenerator;
	String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
	keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
	if (configuration.hasKeyGenerator(keyStatementId)) {
		keyGenerator = configuration.getKeyGenerator(keyStatementId);
	} else {
		keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() 
		&& SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
	} 
	SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
	StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
	Integer fetchSize = context.getIntAttribute("fetchSize");
	Integer timeout = context.getIntAttribute("timeout");
	String parameterMap = context.getStringAttribute("parameterMap");
	//从这条语句中返回的期望类型的类的完全限定名或别名
	String resultType = context.getStringAttribute("resultType");
	Class resultTypeClass = resolveClass(resultType);
	//外部resultMap的命名引用
	String resultMap = context.getStringAttribute("resultMap");
	String resultSetType = context.getStringAttribute("resultSetType");
	ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
	String keyProperty = context.getStringAttribute("keyProperty");
	String keyColumn = context.getStringAttribute("keyColumn");
	String resultSets = context.getStringAttribute("resultSets");
	builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
	fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
	resultSetTypeEnum, flushCache, useCache, resultOrdered,
	keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
public MappedStatement addMappedStatement(String id, SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class parameterType,
String resultMap,
Class resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
	if (unresolvedCacheRef) {
		throw new IncompleteElementException("Cache-ref not yet resolved");
	} 
	id = applyCurrentNamespace(id, false);
	boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
	MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration,id, sqlSource, sqlCommandType)
	.resource(resource)
	.fetchSize(fetchSize)
	.timeout(timeout)
	.statementType(statementType)
	.keyGenerator(keyGenerator)
	.keyProperty(keyProperty)
	.keyColumn(keyColumn)
	.databaseId(databaseId)
	.lang(lang)
	.resultOrdered(resultOrdered)
	.resultSets(resultSets)
	.resultMaps(getStatementResultMaps(resultMap, resultType, id))
	.resultSetType(resultSetType)
	.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
	.useCache(valueOrDefault(useCache, isSelect))
	.cache(currentCache);
	ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
	if (statementParameterMap != null) {
		statementBuilder.parameterMap(statementParameterMap);
	} 
	MappedStatement statement = statementBuilder.build();
	//持有在configuration中
	configuration.addMappedStatement(statement);
	return statement;
}
public void addMappedStatement(MappedStatement ms){
//ms.getId = mapper.UserMapper.getUserById
//ms = MappedStatement等于每一个增删改查的标签的里的数据
	mappedStatements.put(ms.getId(), ms);
}
//最终存放到mappedStatements中,mappedStatements存放的是一个个的增删改查
protected final Map mappedStatements = new StrictMap("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource());

(3)解析bindMapperForNamespace()方法

把 namespace(接口类型)和工厂类绑定起来

private void bindMapperForNamespace() {
	//当前Mapper的命名空间
	String namespace = builderAssistant.getCurrentNamespace();
	if (namespace != null) {
		Class boundType = null;
		try {
			//interface mapper.UserMapper这种
			boundType = Resources.classForName(namespace);
		} catch (ClassNotFoundException e) {} 
		if (boundType != null) {
			if (!configuration.hasMapper(boundType)) {
				configuration.addLoadedResource("namespace:" + namespace);
				configuration.addMapper(boundType);
			}
		}
	}
}
public  void addMapper(Class type) {
	mapperRegistry.addMapper(type);
} 
public  void addMapper(Class type) {
	if (type.isInterface()) {
		if (hasMapper(type)) {
			throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
		} 
		boolean loadCompleted = false;
		try {
			//接口类型(key)->工厂类
			knownMappers.put(type, new MapperProxyFactory<>(type));
			MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
			parser.parse();
			loadCompleted = true;
		} finally {
			if (!loadCompleted) {
				knownMappers.remove(type);
			}
		}
	}
}

(4)(configuration)生成SqlSessionFactory对象

总结
XMLMapperBuilder.parse()方法,是对 Mapper 映射器的解析里面有两个方法:
1.configurationElement()解析所有的子标签,最终解析Mapper.xml中的insert/update/delete/select标签的id(全路径)组成key和整个标签和数据连接组成MappedStatement存放到Configuration中的 mappedStatements这个map
里面。
2.bindMapperForNamespace()是把接口类型(interface mapper.UserMapper)和工厂类存到放MapperRegistry中的knownMappers里面

3.SqlSessionFactory的创建

public SqlSessionFactory build(Configuration config) {
	return new DefaultSqlSessionFactory(config);
}

直接把Configuration当做参数,直接new一个DefaultSqlSessionFactory。

你可能感兴趣的:(MyBatis)