`

ibatis源码学习2_初始化和配置文件解析

 
阅读更多

ibatis整体设计和核心流程 一文中,我们提到了ibatis框架的初始化过程,本文将深入分析ibatis框架的初始化和配置文件解析过程。本文使用的ibatis版本为2.3.4,不同版本间会略有差异。

问题
在详细介绍ibatis初始化过程之前,让我们先来思考几个问题。

1. ibatis初始化的目标是什么?
上文中提到过,ibatis初始化的核心目标是构造SqlMapClientImpl对象,主要是其内部重要属性delegate这个代理对象的初始化。delegate这个对象耦合了用户端的操作行为和执行环境,持有执行操作所需要的所有数据。

2. 如何解析ibatis的sqlmap配置文件和sqlmap映射文件?
可以采用通用的xml文件解析工具,如SAX等。

3. 如何将配置文件中每个节点值注入到SqlMapClientImpl对象中?
可以给不同类型节点设置对应的handler,遍历节点时,调用handler对象的处理方法,将节点值注入到SqlMapClientImpl对象的属性中。


带着上面的这些问题,我们开始探索ibatis的初始化过程。

核心类图
初始化过程主要涉及以下几个重要类,理解这些类的含义非常重要。主要类图如下:

1. Nodelet
该接口是对配置文件中节点处理方式的抽象,该接口仅有一个process()方法,表示对该类节点的处理方式。

2. NodeletParser
同SAX类似的一个xml解析器。不同点在于SAX对所有节点采用同样的处理方式,而NodeletParser可以针对不同的节点个性化配置相 应的处理方式。 NodeletParser内部维护了一个letMap,这个map维护着节点标识信息XPath和对应的处理方式Nodelet的关系。

3. XmlParserState
用于维护配置文件解析过程中的上下文信息,配置文件解析过程中产生的数据都放入XmlParserState中统一维护。
注意: ibatis2.3.0版本中没有这个类,它使用BaseParser的内部类Variables维护上下文信息。

4. SqlMapConfigParser
用于解析sqlMap配置文件 ,其内部组合了NodeletParser对象,用于完成sqlMap配置文件解析。该对象构造函数中,完成向NodeletParser属性中添加sqlMap配置文件 中节点XPath和对应的Nodelet映射关系。比如<typeAlias>节点的处理方式如下:

Java代码  收藏代码
  1. private   void  addTypeAliasNodelets() {  
  2.    parser.addNodelet("/sqlMapConfig/typeAlias" new  Nodelet() {  
  3.      public   void  process(Node node)  throws  Exception {  
  4.        Properties prop = NodeletUtils.parseAttributes(node, state.getGlobalProps()); //解析节点信息   
  5.        String alias = prop.getProperty("alias" );  
  6.        String type = prop.getProperty("type" );  
  7.        state.getConfig().getTypeHandlerFactory().putTypeAlias(alias, type); //向XmlParserState写数据   
  8.      }  
  9.    });  
  10.  }  



5. SqlMapParser
用于解析sqlMap映射文件 ,其内部组合了NodeletParser对象,用于完成sqlMap映射文件解析。和SqlMapConfigParser类似,在构造函数中向NodeletParser属性添加sqlMap映射文件 中节点标识信息XPath和对应的Nodelet映射关系。比如<sql>节点的处理方式如下:

Java代码  收藏代码
  1. private   void  addSqlNodelets() {  
  2.   parser.addNodelet("/sqlMap/sql" new  Nodelet() {  
  3.     public   void  process(Node node)  throws  Exception {  
  4.       Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());  
  5.       String id = attributes.getProperty("id" );  
  6.       if  (state.isUseStatementNamespaces()) {  
  7.         id = state.applyNamespace(id);  
  8.       }  
  9.       if  (state.getSqlIncludes().containsKey(id)) {  
  10.         throw   new  SqlMapException( "Duplicate <sql>-include '"  + id +  "' found." );  
  11.       } else  {  
  12.         state.getSqlIncludes().put(id, node);  
  13.       }  
  14.     }  
  15.   });  
  16. }  



6. SqlStatementParser
用于生成sql对应的MappedStatement对象。其内部仅包含一个public方法parseGeneralStatement(Node node, GeneralStatement statement),用于生成MappedStatement对象。

下面以代码中常见的ibatis配置为例,说明ibatis框架的配置文件解析和初始化过程。
常见ibatis配置

Biz-data-source.xml代码  收藏代码
  1. <bean id= "sqlMapClient"  class= "org.springframework.orm.ibatis.SqlMapClientFactoryBean" >  
  2.     <property name="dataSource"  ref= "dataSource" />  
  3.     <property name="configLocation"  value= "classpath/sqlmap-ibatis.xml" />  
  4. </bean>    

 

配置文件sqlmap-ibatis.xml代码  收藏代码
  1. <sqlMapConfig>  
  2.     <sqlMap resource="sqlmap/cases/CaseSQL.xml" />  
  3.         ...  
  4. </sqlMapConfig>  

 

映射文件casesql.xml代码  收藏代码
  1. <select id= "QUERY-CASE-BY-CASE-ID"   parameterClass= "map"  resultMap= "RM-Case" >       
  2.     SELECT  ID ,GMT_MODIFIED ,GMT_CREATE ,STATUS   
  3.     FROM AVATAR_CASE   
  4.     WHERE ID = #caseId#   
  5. </select>  



初始化过程
1) SqlMapClientFactoryBean初始化
SqlMapClientFactoryBean的初始化方法afterPropertiesSet(),用于构建sqlMapClient对象:

Java代码  收藏代码
  1. public   void  afterPropertiesSet()  throws  Exception {  
  2.     ...  
  3.     this .sqlMapClient = buildSqlMapClient( this .configLocations,  this .mappingLocations,  this .sqlMapClientProperties);    //初始化核心方法,构建sqlMapClient对象       ...   
  4. }  



其中buildSqlMapClient()的实现:

Java代码  收藏代码
  1. protected  SqlMapClient buildSqlMapClient(  
  2.         Resource[] configLocations, Resource[] mappingLocations, Properties properties)  
  3.         throws  IOException {  
  4.                ...  
  5.     SqlMapClient client = null ;  
  6.     SqlMapConfigParser configParser = new  SqlMapConfigParser();  
  7.     for  ( int  i =  0 ; i < configLocations.length; i++) {  
  8.         InputStream is = configLocations[i].getInputStream();  
  9.         try  {  
  10.             client = configParser.parse(is, properties); //通过SqlMapConfigParser解析配置文件,生成SQLMapClientImpl对象   
  11.         }  
  12.     ...  
  13.     return  client;  
  14. }  


上面这段代码中,调用SqlMapConfigParser解析sqlMap配置文件sqlmap-ibatis.xml。

2) 调用SqlMapConfigParser进行解析
SqlMapConfigParser.parse(InputStream inputStream, Properties props)方法源码如下:

Java代码  收藏代码
  1. public  SqlMapClient parse(InputStream inputStream, Properties props) {  
  2.   if  (props !=  null ) state.setGlobalProps(props);  
  3.   return  parse(inputStream);  
  4. }  


其中parse()方法源码如下:

Java代码  收藏代码
  1. public  SqlMapClient parse(InputStream inputStream) {  
  2.     ...  
  3.     parser.parse(inputStream); // 调用NodeletParser解析配置文件   
  4.     return  state.getConfig().getClient();  //返回SqlMapClientImpl对象   
  5.     ...  
  6. }  



3) 调用NodeletParser进行解析
NodeletParser.parse()是配置文件解析的核心方法,这里被SqlMapConfigParser调用,用于解析sqlMap配置文件sqlmap-ibatis.xml。

Java代码  收藏代码
  1. public   void  parse(InputStream inputStream)  throws  NodeletException {  
  2.   try  {  
  3.     Document doc = createDocument(inputStream);  
  4.     parse(doc.getLastChild());  //从根节点开始解析   
  5.   }   
  6.   ...  
  7. }  


其中parse(Node node)方法源码如下:

Java代码  收藏代码
  1. public   void  parse(Node node) {  
  2.   Path path = new  Path();  
  3.   processNodelet(node, "/" );    
  4.   process(node, path);  
  5. }  


这个方法中包含两个重要方法,processNodelet()根据当前节点的XPath进行相应处理,process()方法用于处理该节点相关信息(如Element,Attribute,Children等)。

首先看一下processNodelet(Node node, String pathString)的源码实现:

Java代码  收藏代码
  1. private   void  processNodelet(Node node, String pathString) {  
  2.   Nodelet nodelet = (Nodelet) letMap.get(pathString);  
  3.   if  (nodelet !=  null ) {  
  4.     try  {  
  5.       nodelet.process(node);  
  6.     }   
  7.    ...  
  8.   }  
  9. }  


注意: 在SqlMapConfigParser类介绍中,我们提到过 NodeletParser中letMap属性维护着节点XPath信息和节点信息处理方式的映射关系,所有的映射关系会在 SqlMapConfigParser构造函数中加入。 这里根据节点的XPath,获取对应的处理方式Nodelet,再调用Nodelet.process()处理该节点信息。

再来看一下NodeletParser.process(Node node, Path path)的源码实现:

Java代码  收藏代码
  1. private   void  process(Node node, Path path) {  
  2.   if  (node  instanceof  Element) {  
  3.     //处理Element信息   
  4.     String elementName = node.getNodeName();  
  5.     path.add(elementName);  
  6.     processNodelet(node, path.toString());  
  7.     processNodelet(node, new  StringBuffer( "//" ).append(elementName).toString());  
  8.   
  9.     //处理Attribute信息   
  10.     NamedNodeMap attributes = node.getAttributes();  
  11.     int  n = attributes.getLength();  
  12.     for  ( int  i =  0 ; i < n; i++) {  
  13.       Node att = attributes.item(i);  
  14.       String attrName = att.getNodeName();  
  15.       path.add("@"  + attrName);  
  16.       processNodelet(att, path.toString());  
  17.       processNodelet(node, new  StringBuffer( "//@" ).append(attrName).toString());  
  18.       path.remove();  
  19.     }  
  20.   
  21.     // 处理Children信息   
  22.     NodeList children = node.getChildNodes();  
  23.     for  ( int  i =  0 ; i < children.getLength(); i++) {  
  24.       process(children.item(i), path); //递归处理子节点   
  25.     }  
  26.     path.add("end()" );  
  27.     processNodelet(node, path.toString());  
  28.     path.remove();  
  29.     path.remove();  
  30.   } else   if  (node  instanceof  Text) {  
  31.     // Text   
  32.     path.add("text()" );  
  33.     processNodelet(node, path.toString());  
  34.     processNodelet(node, "//text()" );  
  35.     path.remove();  
  36.   }  
  37. }  


该方法中依次处理节点的Element、Attribute和Children信息,处理Children信息时采用的是递归的方式。处理每个节点时,首先构建当前节点的XPath信息,再调用processNodelet(node, path)对该节点进行处理。

4) 调用SqlMapParser解析sqlMap映射配置文件CaseSQL.xml。
当处理sqlMap配置文件sqlmap-ibatis.xml中的sqlMap节点时,会调用下面的Nodelet进行处理:

Java代码  收藏代码
  1.   protected   void  addSqlMapNodelets() {  
  2.    parser.addNodelet("/sqlMapConfig/sqlMap" new  Nodelet() {  
  3.      public   void  process(Node node)  throws  Exception {  
  4.        ...  
  5.        Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());  
  6.   
  7.        String resource = attributes.getProperty("resource" );  
  8.        String url = attributes.getProperty("url" );  
  9.        ...  
  10.        new  SqlMapParser(state).parse(reader);  //调用SqlMapParser解析sqlMap映射文件   
  11.        }  
  12.      }  
  13.    });  
  14. }  



SqlMapParser的parse()方法实现如下

Java代码  收藏代码
  1. public   void  parse(InputStream inputStream)  throws  NodeletException {  
  2.   parser.parse(inputStream);//调用NodeletParser解析配置文件   
  3. }  


这里和上面的SqlMapConfigParser解析方式类似,都是调用NodeletParser解析配置文件,不同点在于这两个类是针对不同的配置文件解析(SqlMapConfigParser针对sqlMap配置文件,SqlMapParser针对sqlMap映射文件) ,所以在各自构造函数插入letMap时,使用的key是自己配置文件里节点的XPath。

5) 调用SqlStatementParser生成MappedStatement。
当解析sqlMap映射文件的select节点时,将调用SqlStatementParser生成MappedStatement。

Java代码  收藏代码
  1. protected   void  addStatementNodelets() {  
  2.   ...  
  3.   parser.addNodelet("/sqlMap/select" new  Nodelet() {  
  4.     public   void  process(Node node)  throws  Exception {  
  5.       statementParser.parseGeneralStatement(node, new  SelectStatement());  
  6.     }  
  7.     
  8.   });  


SqlStatementParser.parseGeneralStatement()的实现这里就不详述了,主要目的是构建MappedStatement,并将配置文件解析后的信息注入到XmlParserState中。

小结
ibatis初始化的核心目标是构建SqlMapClientImpl对象,主要思想如下:
1. 构建NodeletParser配置文件解析类,它维护了节点XPath和对应处理方式的映射关系,并提供了节点的通用处理方法。
2. SqlMapConfigParser和SqlMapParser在构造方法中向NodeletParser中添加节点XPath和对应处理方式的映射关系。
3. 配置文件解析采用递归方式进行,首先生成当前节点的XPath信息,再从NodeletParser获取对应的处理方式并执行。
4. 整个解析过程中每个节点生成的数据统一注入到XmlParserState,最终通过XmlParserStat获取SqlMapClientImpl对象并返回。

分享到:
评论

相关推荐

    ibatis-sqlmap_2.3.4_2.jar两个版本的jar包

    2. 创建SqlMapClient:根据SqlMapConfig.xml配置文件初始化SqlMapClient实例。 3. 加载SqlMap文件:每个SqlMap文件对应一个数据库表或视图,定义SQL语句和参数映射。 4. 执行SQL:通过SqlMapClient实例调用相应的...

    ibatis实战之基础环境搭建(源码).zip

    - 如何解析SqlMapConfig.xml配置文件,创建SqlSessionFactory? - SqlSession是如何管理数据库会话的? - Executor接口及其实现如何处理SQL执行? - 参数和结果是如何通过ParameterHandler和ResultHandler进行映射的...

    ibatis源代码项目

    《深入解析iBatis源代码》 iBatis,作为一个轻量级的持久...通过解析SqlSessionFactory、SqlSession、Mapper等核心组件,以及XML映射文件解析和动态SQL的实现,我们可以更好地运用iBatis,提高项目开发的效率和质量。

    ibatis3源码+驱动

    1. SqlSessionFactoryBuilder:它是用于创建SqlSessionFactory的对象,通常在应用初始化时使用,通过SqlSessionFactoryBuilder,我们可以设置数据源、配置文件等信息,构建出适合项目的SqlSessionFactory。...

    ibatis 使用手册

    开发指南会深入讲解如何初始化iBatis环境,包括设置配置文件、创建SqlMap文件、设计Mapper接口以及如何在Java代码中使用这些组件。此外,还会介绍如何处理结果集、执行批量操作、使用缓存机制等高级特性。 **iBatis...

    iBatis学习

    - 配置加载:iBatis在初始化时读取配置文件,加载SQL映射和映射器接口。 - SQL执行:通过调用映射器接口的方法,iBatis会查找对应的SQL映射,动态生成SQL并执行。 - 数据转换:查询结果会被自动映射到Java对象,...

    spring mvc+ibatis 框架 简单demo

    "spring mvc+ibatis 框架 简单demo"这个项目可能涵盖了从项目初始化、Spring MVC控制器设计、iBatis数据库交互、Service层业务逻辑到视图展示的整个流程。通过这个DEMO,开发者可以学习到如何整合这两个框架,理解...

    spring2.5+ibatis3+web service cxf 例子MyEclipse工程

    1. spring-context-3.0.4.RELEASE.jar - 包含了Spring的应用上下文,这是Spring的核心模块,负责初始化、配置和管理Bean。 2. spring-beans-3.0.4.RELEASE.jar - 提供了Bean工厂,它是Spring的基础,用于创建、配置...

    proxool源码

    - `PoolConfig`类:负责解析配置文件,并维护连接池的相关属性。 2. **连接管理**: - `PoolAlias`:定义了数据库连接池的别名,每个连接池都有一个唯一的别名。 - `ProxyConnection`:代理连接,它是对真实...

    Spring技术内幕 学习笔记

    这包括Bean的生命周期管理,依赖注入,以及如何初始化和销毁Bean。 3. **Spring基于 Annotation 的简单介绍**: - 这里会概述Spring的注解驱动开发,包括@Component、@Service、@Repository和@Controller等核心...

    spring2.5源码

    3. **Bean生命周期管理**:Spring管理bean的生命周期,包括初始化、销毁方法以及各种生命周期回调。在2.5版本中,可以通过`@PostConstruct`和`@PreDestroy`注解来指定这些方法。 4. **资源管理**:Spring 2.5引入了...

    Spring2.0 核心技术与最佳实践 随书源码

    3. **JSR-250支持**:Spring2.0引入了对JSR-250规范的支持,例如`@PostConstruct`和`@PreDestroy`注解,用于标记初始化和销毁方法,增强了组件生命周期的管理。 4. **Web MVC框架**:Spring的MVC框架在2.0版本中...

    Spring Framework 1.x.x 版本完整最后一版

    BeanFactory是Spring的基础,负责管理bean的创建、初始化、配置和查找。ApplicationContext则扩展了BeanFactory,增加了对国际化、事件传播、资源加载等功能。 3. **数据访问/集成**: Spring支持多种数据访问技术...

    Spring 2.0 源代码

    5. **JSR-250注解支持**:Spring 2.0开始支持JSR-250标准注解,如`@PostConstruct`和`@PreDestroy`,使得初始化和清理方法的标记更为直观,与EJB3.0兼容性增强。 6. **国际化(Internationalization, i18n)**:...

Global site tag (gtag.js) - Google Analytics