mybatis源码解析(二)初始化SqlSessionFactory对象
作者:互联网
上篇我们准备好了debug环境,这篇我们具体深入分析下SqlSessionFactory 的创建
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
1、SqlSessionFactoryBuilder创建SqlSessionFactory对象
调用重载方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
创建XMLConfigBuilder对象,调用parse方法解析XML配置文件
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.
}
}
}
解析成Configuration对象,然后再调用重载方法,创建SqlSessionFactory对象
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2、分析XMLConfigBuilder的parse方法,解析配置文件
public Configuration parse() {
if (parsed) { // 第一次进入解析时,会在下面将parsed设置true,保证只能解析一次
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration(parser.evalNode("/configuration"));
解析配置文件configuration标签,我们主要分析下第一篇中配置的properties,environments,mappers是三个标签
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
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"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
1)properties标签解析
解析resource和url方式引入的配置属性,而且如果resource和url同时不为null就会抛出BuilderException,证明只能使用其中的一种。最后将这些属性保存到configuration对象中,例子中配置的这些属性是environments中的数据源属性
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
// 解析properties标签是否有resource属性
String resource = context.getStringAttribute("resource");
// 解析properties标签是否有url属性
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
// 将properties属性保存到configuration对象
configuration.setVariables(defaults);
}
}
2)environments标签解析
1、解析transactionManager子标签type类型为JDBC,创建事务工厂,用于创建事务
2、解析dataSource子标签属性type为POOLED,创建数据源工厂,用于创建数据源
3、封装事务工厂和数据源到Environment对象中,set到Configuration对象中
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// 创建事务工厂
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 创建数据源工厂
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
// 获取数据源
DataSource dataSource = dsFactory.getDataSource();
// 通过Builder创建Environment对象
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 设置Environment对象到configuration对象中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
3)mappers标签解析
通过package,resource,url,class四种方式加载mapper文件,由于我们配置的是resource方式,通过XMLMapperBuilder加载的配置文件,所以直接分析mapperParser.parse();
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");
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) {
// *********************resource解析mapper文件逻辑*************************
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
// *********************resource解析mapper文件逻辑*************************
} 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.");
}
}
}
}
}
4)mapperParser.parse(); 解析mapper标签
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper标签,解析sql封装MappedStatement对象保存到configuration中
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 创建Mapper接口的代理工厂类,扫描Mapper接口方法的注解,封装sql
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
1、configurationElement(parser.evalNode("/mapper"));
1)解析获取mapper文件的namespace
2)解析sql(SqlSource 封装)调用栈如下
buildStatementFromContext(list, null);
statementParser.parseStatementNode();
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
builder.parseScriptNode();
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
3)将SqlSource和一系列参数封装到MappedStatement,通过 configuration.addMappedStatement(statement);保存到configuration对象中,后续在调用mapper接口方法时,会从这里取出sql执行
2、bindMapperForNamespace(); // 绑定mapper接口和对应的mapper代理类
1)调用栈如下,后续可以通过sqlSession对象根据mapper类型来获取对应的mapper代理对象
configuration.addMapper(boundType);
mapperRegistry.addMapper(type);
knownMappers.put(type, new MapperProxyFactory<>(type));
2)下面还有一个很重要的处理逻辑,就是读取mapper接口方法的注解,创建SqlSource对象,最后再封装成MappedStatement对象保存到configuratioin中mappedStatements属性中。
调用栈如下
knownMappers.put(type, new MapperProxyFactory<>(type));
// 在put代理工厂后
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
parseStatement(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
buildSqlSourceFromStrings(strings, parameterType, languageDriver);
assistant.addMappedStatement(...)
5)至此XMLConfigBuilder的parse方法解析完毕,返回configuration对象,再调用build方法创建DefaultSqlSessionFactory对象,SqlSessionFactory初始化完成
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
标签:SqlSessionFactory,resource,url,解析,源码,configuration,mybatis,null,evalNode 来源: https://blog.csdn.net/weixin_41645232/article/details/113666911