Mybatis源码分析之SqlSessionFactory的创建
作者:互联网
Mybatis源码分析(一)
Mybatis的运行过程主要分为两步,第一步读取配置文件将配置缓存到Configuration对象,用于构建SqlSessionFactory,第二步为SqlSession的执行过程。其中SqlSession的过程会比较难,而第一步相对来说比较容易看懂,相对简单点。
以普通案例开始
1 @Test 2 public void findById() throws Exception { 3 String resource = "mybatis-config.xml"; 4 InputStream inputStream = Resources.getResourceAsStream(resource); //构建一个输入流对象 5 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //在该地方打断点来分析 6 SqlSession sqlSession = sessionFactory.openSession(); 7 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 8 System.out.println(userMapper.findById(1)); 9 }
SqlSessionFactory构建的过程
首先进入到了SqlSessionFactoryBuilder类中的build方法中来
1 public SqlSessionFactory build(InputStream inputStream) { 2 return build(inputStream, null, null); 3 }
该方法中调用了另外一个build方法
1 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { 2 try { 3 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 4 return build(parser.parse()); 5 } catch (Exception e) { 6 throw ExceptionFactory.wrapException("Error building SqlSession.", e); 7 } finally { 8 ErrorContext.instance().reset(); 9 try { 10 inputStream.close(); 11 } catch (IOException e) { 12 // Intentionally ignore. Prefer previous error. 13 } 14 } 15 }
我们看到了一个可疑对象XMLConfigBuilder类,继续跟踪进去
1 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { 2 this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); 3 }
猜测这里应该是做XML校验的,跳过,创建好了XMLConfigBuilder对象,接下来进行parse()了
1 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 2 return build(parser.parse());
继续跟踪进入XMLConfigBuilder中的parse()
1 public Configuration parse() { 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 }
跟进该类的parseConfiguration方法中
1 private void parseConfiguration(XNode root) { 2 try { 3 //issue #117 read properties first 4 propertiesElement(root.evalNode("properties")); 5 Properties settings = settingsAsProperties(root.evalNode("settings")); 6 loadCustomVfs(settings); 7 loadCustomLogImpl(settings); 8 typeAliasesElement(root.evalNode("typeAliases")); 9 pluginElement(root.evalNode("plugins")); 10 objectFactoryElement(root.evalNode("objectFactory")); 11 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 12 reflectorFactoryElement(root.evalNode("reflectorFactory")); 13 settingsElement(settings); 14 // read it after objectFactory and objectWrapperFactory issue #631 15 environmentsElement(root.evalNode("environments")); //在这里读取数据源的信息封装 16 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 17 typeHandlerElement(root.evalNode("typeHandlers")); 18 mapperElement(root.evalNode("mappers")); //这里是解析我们定义的Mapper.xml 19 } catch (Exception e) { 20 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 21 } 22 }
看到这里应该舒服了吧 这不就是我们的mybatis-config配置文件的内容吗。
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <!-- 根标签 --> 6 <configuration> 7 <properties></properties> 8 <settings> 9 <setting name="cacheEnabled" value="true"/> 10 </settings> 11 <typeAliases></typeAliases> 12 <typeHandlers></typeHandlers> 13 <objectFactory type=""></objectFactory> 14 <objectWrapperFactory type=""></objectWrapperFactory> 15 <reflectorFactory type=""></reflectorFactory> 16 <plugins> 17 <plugin interceptor=""></plugin> 18 </plugins> 19 <!-- 环境,可以配置多个,default:指定采用哪个环境 --> 20 <environments default="test"> 21 <!-- id:唯一标识 --> 22 <environment id="test"> 23 <!-- 事务管理器,JDBC类型的事务管理器 --> 24 <transactionManager type="JDBC" /> 25 <!-- 数据源,池类型的数据源 --> 26 <dataSource type="POOLED"> 27 <property name="driver" value="com.mysql.jdbc.Driver" /> 28 <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdemo" /> 29 <property name="username" value="root" /> 30 <property name="password" value="123456" /> 31 </dataSource> 32 </environment> 33 </environments> 34 <databaseIdProvider type=""></databaseIdProvider> 35 <mappers> 36 <mapper resource="mappers/UserMapper.xml" /> 37 </mappers> 38 </configuration>
接下来我们细看一下mapperElement方法
1 private void mapperElement(XNode parent) throws Exception { 2 if (parent != null) { 3 for (XNode child : parent.getChildren()) { 4 //如果是package的方法加载则进入package分支 5 if ("package".equals(child.getName())) { 6 String mapperPackage = child.getStringAttribute("name"); 7 configuration.addMappers(mapperPackage); 8 } else { 9 String resource = child.getStringAttribute("resource"); 10 String url = child.getStringAttribute("url"); 11 String mapperClass = child.getStringAttribute("class"); 12 //如果是resource的方法加载则进入resource分支 13 if (resource != null && url == null && mapperClass == null) { 14 ErrorContext.instance().resource(resource); 15 InputStream inputStream = Resources.getResourceAsStream(resource); 16 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); 17 mapperParser.parse(); 18 //如果是url的方法加载则进入url分支 19 } else if (resource == null && url != null && mapperClass == null) { 20 ErrorContext.instance().resource(url); 21 InputStream inputStream = Resources.getUrlAsStream(url); 22 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); 23 mapperParser.parse(); 24 //进入class分支 25 } else if (resource == null && url == null && mapperClass != null) { 26 Class<?> mapperInterface = Resources.classForName(mapperClass); 27 configuration.addMapper(mapperInterface); 28 } else { 29 throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); 30 } 31 } 32 } 33 } 34 }
Mybatis的mapper.xml有四种加载方式
1 <mappers> 2 <mapper resource="mappers/UserMapper.xml" /> 3 <mapper class=""></mapper> 4 <mapper url=""></mapper> 5 <package name=""></package> 6 </mappers>
所以上面的代码是在选择哪种方式加载mapper.xml文件。我们定义的是resource方式,所以进入resource分支中去。
1 if (resource != null && url == null && mapperClass == null) { 2 ErrorContext.instance().resource(resource); 3 InputStream inputStream = Resources.getResourceAsStream(resource); 4 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); //构建mapper解析器 5 mapperParser.parse(); //解析mapper.xml的逻辑开始 6 } 7 8 //进入XMLMapperBuilder类找那个的方法 9 public void parse() { 10 if (!configuration.isResourceLoaded(resource)) { //如果是resource加载进入 11 configurationElement(parser.evalNode("/mapper")); 12 configuration.addLoadedResource(resource); 13 bindMapperForNamespace(); //通过namespace绑定mapper 14 } 15 16 parsePendingResultMaps(); //解析resultMap标签 17 parsePendingCacheRefs(); //解析cache-ref标签 18 parsePendingStatements(); //其他的标签 19 }
执行完这些之后,将这些配置文件全部封装在configuration对象中了,接着返回该对象进入SqlSessionFactoryBuilder的build方法中
1 public SqlSessionFactory build(Configuration config) { 2 return new DefaultSqlSessionFactory(config); 3 } 4 5 public class DefaultSqlSessionFactory implements SqlSessionFactory { 6 7 private final Configuration configuration; 8 9 public DefaultSqlSessionFactory(Configuration configuration) { 10 this.configuration = configuration; 11 } 12 ...... 13 }
可以看到DefaultSqlSessionFactory是SqlSessionFactory的一个实现类,里面包含了Configuration对象。到这里已经构建完成了SqlSessionFactory了
总结
SqlSessionFactory的构建过程Configuration类起了很大作用,几乎所以的配置都能在该类中找到。有兴趣的同学可以去看看。
标签:SqlSessionFactory,resource,null,inputStream,源码,Mybatis,new,configuration,evalNod 来源: https://www.cnblogs.com/lgxblog/p/11578295.html