mybatis源码学习:从SqlSessionFactory到代理对象的生成

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

mybatis源码学习:从SqlSessionFactory到代理对象的生成

天乔巴夏丶   2020-04-25 我要评论
[toc] # 一、根据XML配置文件构建SqlSessionFactory 一、首先读取类路径下的配置文件,获取其字节输入流。 二、创建SqlSessionFactoryBuilder对象,调用内部的build方法。`factory = new SqlSessionFactoryBuilder().build(in);` 三、根据字节输入流创建XMLConfigBuilder即解析器对象parser。`XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);` ```java public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //根据字节输入流创建XMLConfigBuilder即解析器对象parser XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //返回的Configuration配置对象作为build的参数 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方法,`parser.parse()`,该结果将返回一个Configuration配置对象,作为build方法的参数。 五、parse()方法中,调用parseConfiguration方法将Configuration元素下的所有配置信息封装进Parser对象的成员Configuration对象之中。 ```java public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //将configuration的配置信息一一封装到configuration中 parseConfiguration(parser.evalNode("/configuration")); return configuration; } ``` 六、其中进行解析xml元素的方式是将通过evalNode方法获取对应名称的节点信息。如:`parseConfiguration(parser.evalNode("/configuration"));`,此时`parser.evalNode("/configuration")`即为Configuration下的所有信息。 七、parseConfiguration方法相当于将里面每个元素的信息都单独封装到Configuration中。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419174956516.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70) 值得一提的是,我们之后要分析基于代理模式产生dao的代理对象涉及到mappers的封装,其实也在配置文件读取封装的时候就已经完成,也就是在parseConfiguration方法之中:`mapperElement(root.evalNode("mappers"));`。他的作用就是,读取我们主配置文件中``的元素内容,也就是我们配置的映射配置文件。 ```xml ``` `private void mapperElement(XNode parent)`方法将mappers配置下的信息获取,此处获取我们resources包下的com.smday.dao包名。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175025870.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70) 接着就调用了configuration的addMappers方法,其实还是调用的是mapperRegistry。 ```java public void addMappers(String packageName) { mapperRegistry.addMappers(packageName); } ``` 读到这里,我们就会渐渐了解MapperRegistry这个类的职责所在,接着来看,这个类中进行的一些工作,在每次添加mappers的时候,会利用ResolverUtil类查找类路径下的该包名路径下,是否有满足条件的类,如果有的话,就将Class对象添加进去,否则报错。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2020041917504365.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70) ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175051488.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70) 紧接着,就到了一步比较重要的部分,当然只是我个人觉得,因为第一遍看的时候,我没有想到,这步居然可以封装许许多多的重要信息,我们来看一看: ```java 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 { //将接口类作为键,将MapperProxyFactory作为值存入 knownMappers.put(type, new MapperProxyFactory(type)); // 在运行解析器之前添加类型十分重要,否则可能会自动尝试绑定映射器解析器 // 如果类型已知,则不会尝试 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); //解析mapper映射文件,封装信息 parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } ``` 映射配置文件的读取依靠namespace,我们可以通过查看源码发现读取映射配置文件的方法是loadXmlResouce(),所以namespace命名空间至关重要: ```java 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 // 防止加载两次,可以发现这句 判断在许多加载资源文件的时候出现 if (!configuration.isResourceLoaded("namespace:" + type.getName())) { String xmlResource = type.getName().replace('.', '/') + ".xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource); } catch (IOException e) { // ignore, resource is not required } if (inputStream != null) { XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); //最终解析 xmlParser.parse(); } } } ``` ```java //xmlPaser.parse() public void parse() { if (!configuration.isResourceLoaded(resource)) { //读取映射配置文件信息的主要代码 configurationElement(parser.evalNode("/mapper")); //加载完成将该路径设置进去,防止再次加载 configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175110858.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70) 可以看到,对映射文件解析之后,mappedStatements对象中出现了以下内容: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175130650.png) 至此,主配置文件和映射配置文件的配置信息就已经读取完毕。 八、最后依据获得的Configuration对象,创建一个`new DefaultSqlSessionFactory(config)`。 ```java public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } ``` 总结: - 解析配置文件的信息,并保存在Configuration对象中。 - 返回包含Configuration的DefaultSqlSession对象。 # 二、通过SqlSessionFactory创建SqlSession 一、调用SqlSessionFactory对象的openSession方法,其实是调用`private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)`方法,通过参数就可以知道,分别是执行器的类型,事务隔离级别和设置是否自动提交,因此,我们就可以得知,我们在创建SqlSession的时候可以指定这些属性。 ```java private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //获取Environment信息 final Environment environment = configuration.getEnvironment(); //获取TransactionFactory信息 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //创建Transaction对象 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //创建执行器对象Executor final Executor executor = configuration.newExecutor(tx, execType); //创建DefaultSqlSession对象并返回 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } ``` 二、从configuration中获取environment、dataSource和transactionFactory信息,创建事务对象Transaction。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175352133.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70) 补充:后续看了一些博客,说是保证executor不为空,因为defaultExecutorType有可能为空。 三、根据配置信息,执行器信息和自动提交信息创建DefaultSqlSession。 # 三、getMapper获取动态代理对象 下面这句话意思非常明了,就是通过传入接口类型对象,获取接口代理对象。 `IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);` 具体的过程如下: 一、首先,调用SqlSession的实现类DefaultSqlSession的getMapper方法,其实是在该方法内调用configuration的getMapper方法,将接口类对象以及当前sqlsession对象传入。 ```java //DefaultSqlSession.java @Override public T getMapper(Class type) { //调用configuration的getMapper return configuration.getMapper(type, this); } ``` 二、接着调用我们熟悉的mapperRegistry,因为我们知道,在读取配置文件,创建sqlSession的时候,接口类型信息就已经被存入到其内部维护的Map之中。 ```java //Configuration.java public T getMapper(Class type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175618941.png) 三、我们来看看getMapper方法具体的实现如何: ```java public T getMapper(Class type, SqlSession sqlSession) { //根据传入的类型获取对应的键,也就是这个代理工厂 final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { //最终返回的是代理工厂产生的一个实例对象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } ``` 四、紧接着,我们进入MapperProxyFactory,真真实实地发现了创建代理对象的过程。 ```java protected T newInstance(MapperProxy mapperProxy) { //创建MapperProxy代理对象 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { //MapperProxy是代理类, final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } ``` ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200419175643800.png)

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们