Mybaties源碼分析

Mybatis的配置文件主要有二個,分析爲總的配置文件和Mapper的配置文件

總的配置文件的主要配置如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration  
	  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
	  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<!-- 解析從這個根開始,是一個寫死的配置 -->	    
<configuration>  
	    <properties resource="db.properties"></properties>  
	    <environments default="development">  
	        <environment id="development">  
	            <transactionManager type="JDBC"></transactionManager>  
	            <dataSource type="POOLED">  
	                <property name="driver" value="${mybaties.driver}"/>  
	                <property name="url" value="${mybaties.url}"/>  
	                <property name="username" value="${mybaties.username}"/>  
	                <property name="password" value="${mybaties.password}"/>  
	            </dataSource>  
	        </environment>  
	    </environments>  
	    <mappers>  
	        <mapper resource="UserMapper.xml"/>  
	    </mappers>   
</configuration>

db.properties的配置內容如下

mybaties.driver=com.mysql.jdbc.Driver
mybaties.url=jdbc:mysql://localhost:3306/crm
mybaties.username="root"
mybaties.password=123456

Mapper的配置文件的內容如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.hailong.user"> 
					 
    <resultMap type="com.hailong.mybaties.User" id="detailUserResultMap">  
        <constructor>  
            <idArg column="user_id" javaType="String"/>  
            <arg column="user_name"/>  
        </constructor>  
          
        <result property="password" column="user_pwd" />  
        <result property="type" column="user_type" javaType="com.hailong.mybaties.UserType"/>  
        <result property="svcnum" column="svc_num" />  
          
        <association property="cust" javaType="com.hailong.mybaties.Cust">  
            <id property="id" column="cust_id"/>  
            <result property="custname" column="cust_name"/>  
            <result property="certNo" column="cert_no"/>  
        </association>  
          
        <collection property="accts" column="" ofType="com.hailong.mybaties.Acct">  
            <id property="id" column="acct_id" />  
            <result property="payName" column="pay_name"/>  
            <result property="bankNo" column="bank_no"/>  
        </collection>  
      
    </resultMap>  
    <select id="selectUserDetail" resultMap="detailUserResultMap"> 
            select id,user_name,user_type,cust_id from user a where a.user_id=#{userId} 
    </select>  
</mapper> 

Mybaties的測試代碼如下

import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SqlSesstionFactoryTest  
{  
  
    public static void main(String[] args) throws Exception  
    {  
        String resouce="mybatis-config.xml";
        //這個resource本身就會到類路徑下去找這個配置文件
        InputStream is=Resources.getResourceAsStream(resouce);  
        //這裏是正式開始讀取配置文件
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is); //1
        //進行操作
        //SqlSession sqlSession=sqlSessionFactory.openSession();
        
        System.out.println(sqlSessionFactory.getConfiguration());  
    }  
}  

new SqlSessionFactoryBuilder()什麼內容都沒有做,只是單單創建了一個SqlSessionFactoryBuilder對象,在這個對象的內部提供了大量的bulider方法,主要是用來創建這個SqlSessionFactory對象的,同時這個SqlSessionFactory對象的創建主要是使用了創建類設計模式的Builder設計模式,主要是用來構造一個複雜的對象,其實在Mybatis當中很多類對象的創建使用了Builder設計模式,下面就是這個就是創建我們的SqlSessionFactory對象所有方式。


其實在創建這個SqlSessionFactory對象的同時,也就是解析Mybatis的二個配置文件的過程封裝到我們的Configration對象當中,如下圖所示

修改測試代碼如下

import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SqlSesstionFactoryTest  
{  
  
    public static void main(String[] args) throws Exception  
    {  
        String resouce="mybatis-config.xml";
        //這個resource本身就會到類路徑下去找這個配置文件
        InputStream is=Resources.getResourceAsStream(resouce);  
        //這裏是正式開始讀取配置文件
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is); 
        
        //得到解析mybatis的二個配置文件的所有的內容
        Configuration config=sqlSessionFactory.getConfiguration();
        
        System.out.println(config);
        
        //進行操作
        SqlSession sqlSession=sqlSessionFactory.openSession();
        
       
        
        System.out.println(sqlSessionFactory.getConfiguration());  
    }  
}  
結果如下:



這個datasources當中的內容和我們的db.properties的內容一樣,MappedStatement中的內容和我們的userMapper.xml文件中的內容一樣,也就是說創建這個SqlSessionFactory對象的同時解析Mybatis的二個配置文件的過程封裝到我們的Configration對象當中

下面開始進行Debuger我們的Mybatis源碼

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {//主要是創建XMLConfigBuilder對象來解析mybatis的核心配置文件(mybatisconfig.xml)
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());//1 進行看完後發現 parser.parse()返回的是一個Configration對象,也就是說他把
//mybatisconfig.xml已經封裝到了Configration對象當中了 } 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()進行解析我們的mybatisconfig.xml當中的內容,這個文件當中的所有內容已經全部封裝到了InputStream流當中了

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true; //表示已經用過了個XMLConfigBuilder解析過這個配置文件,也說明了這個配置文件在開啓時只解析一次
    //下面就是解析這個配置文件的具體過程 /configuration是我們的mybatis配置文件的根標籤<configuration> ,
  //同時這個parser.evalNode("/configration")解析mybatiesconfig.xml文件,然後返回這個配置文件的根標籤元素
    parseConfiguration(parser.evalNode("/configuration"));//parseConfiguration(Node root) /內封裝的是解析根標籤下的子標籤的內容
    return configuration;
  }

這個evalNode(string express)是根據配置文件當中標籤的名字來獲取相應的標籤結點

  public XNode evalNode(String expression) {
    return evalNode(document, expression);
  }
  //XNode返回的是我們的根結點 configration
  public XNode evalNode(Object root, String expression) {
    Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
    if (node == null) {
      return null;
    }
    return new XNode(this, node, variables);
  }

對應我們的mybatisconfig.xml的根標籤configration,同理其他的結點是在這個根結點的基礎下進行解析(下面會講到)

接下來就是在這個根標籤的基礎上進行解析這個配置文件了,具體細節來看這個parseConfiguration(XNode root)方法,也就是解析這個配置文件當中的子內容

  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      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);
    }
  }

先來看一下這個XMLConfigBulider的所有解析mybatiesconfig.xml的方法


第一個parse()方法獲取根標籤

第二個parseConfiguration(XNode)是解析mybatisconfig.xml的核心所有,在這個方法當中調用了所有的XMLConfigBuilder的解析方法

第三個propertiesElement(XNode)是解析我們的properties屬性,同時封裝到Confuration當中


這裏有一個知識點就是我們的Properties文件的resource和url屬性只能配置一個,不能同時配置二個

下面看一下getResourceAsProperties()的代碼如下

  /*
   * Returns a resource on the classpath as a Properties object
   *
   * @param resource The resource to find
   * @return The resource
   * @throws java.io.IOException If the resource cannot be found or read
   */
  public static Properties getResourceAsProperties(String resource) throws IOException {
    Properties props = new Properties();
    InputStream in = getResourceAsStream(resource);
    props.load(in);
    in.close();
    return props;
  }







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章