mybatis初始化過程

mybatis初始化了什么

mybatis初始化工作,主要是初始化configuration配置信息,主要分為兩部分

  • 從mybatis-config.xml中讀取的配置
  • 從mapper配置文件或Mapper注解讀取的配置

代碼分析

mybatis初始化要經(jīng)過簡單的以下幾步:

  1. 調(diào)用SqlSessionFactoryBuilder對象的build(inputStream)方法腹尖;

  2. SqlSessionFactoryBuilder會根據(jù)輸入流inputStream等信息創(chuàng)建XMLConfigBuilder對象;

  3. XMLConfigBuilder對象初始化Configuration對象叫倍;

  4. XMLConfigBuilder調(diào)用parse方法,讀取配置文件信息,更改Configuration對象屬性信息良瞧,并返回Configuration對象

  5. SqlSessionFactoryBuilder根據(jù)Configuration對象創(chuàng)建一個DefaultSessionFactory對象;

  6. SqlSessionFactoryBuilder返回 DefaultSessionFactory對象給Client,供Client使用蹋偏。

  7. 讀取配置文件流程圖


    configuration初始化流程.png

XMLConfigBuilder解析配置文件,并返回Configuration源碼解析

 public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      // xmlConfigBuilder讀取配置文件信息至壤,初始話Configuration信息
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      //xmlConfigBuilder調(diào)用parse解析配置文件信息威始,返回Configuration
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
//通過XPathParser解析xml    
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //new出一個Configuration對象,對象中有初始化的屬性信息
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //解析configuration節(jié)點下的配置信息
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //讀取properties屬性信息
      propertiesElement(root.evalNode("properties"));
      //讀取別名信息
      typeAliasesElement(root.evalNode("typeAliases"));
      //讀取插件信息
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
     //讀取全局配置屬性信息
      settingsElement(root.evalNode("settings"));
      // 多環(huán)境讀取配置
      environmentsElement(root.evalNode("environments"));
      //多數(shù)據(jù)源配置讀取
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        //讀取類型轉(zhuǎn)換器配置信息
      typeHandlerElement(root.evalNode("typeHandlers"));
       //讀取mappers信息像街,包含resultMpper,sql語句信息
       mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

propertiesElement源碼解析

private void propertiesElement(XNode context) throws Exception {
    //對properties節(jié)點下配置信息進行解析
    if (context != null) {
      /*讀取properties節(jié)點下所有的<property name="" value=""/>子節(jié)點
        *并以name為key,value為key的值注入到一個properties中黎棠,并返回
        */
      Properties defaults = context.getChildrenAsProperties();
      //獲取properties節(jié)點下的節(jié)點resource配置信息路徑
      String resource = context.getStringAttribute("resource");
      //獲取properties節(jié)點下的節(jié)點url配置信息路徑
      String url = context.getStringAttribute("url");
      //url和resource只能配置一個,否則拋出錯誤
      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) {
        /*讀取resource路徑的配置信息镰绎,并返回脓斩,如果普通<property name=""           
        *value=""/> 有重復key配置,則以resource配置為準
      */
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
          /*讀取url路徑的配置信息畴栖,并返回随静,如果普通<property name=""           
        *value=""/> 有重復key配置,則以url 配置為準
      */
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //此為初始化外部傳入的properties配置信息
      Properties vars = configuration.getVariables();
      if (vars != null) {
         //配置文件配置信息 與 外部傳入配置信息合并驶臊,如果有重復key配置挪挤,則以外部為準
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      //更新configuration的屬性Variables的值
      configuration.setVariables(defaults);
    }
  }

typeAliasesElement源碼解析

  private void typeAliasesElement(XNode parent) {
    //解析節(jié)點typeAliases節(jié)點配置信息
    /*
   <typeAliases>
        <typeAlias type="com.study.entity.User" alias="User"/>
        <package name="com.study.entity"/>
    </typeAliases>
    */
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
           //解析掃描包,進行別名配置
          String typeAliasPackage = child.getStringAttribute("name");
          // 掃描包下面的類关翎,進行別名配置
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          //獲取別名
          String alias = child.getStringAttribute("alias");
          //獲取別名 對應的類名稱
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
            //注冊別名   typeAliasRegistry對象和configuration.getTypeAliasRegistry()是同一個
              typeAliasRegistry.registerAlias(clazz);
            } else {
              //注冊別名 typeAliasRegistry對象和configuration.getTypeAliasRegistry()是同一個
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }
  public void registerAliases(String packageName){
    //根據(jù)包名扛门,掃描包下面的類 ,進行注冊別名配置
    registerAliases(packageName, Object.class);
  }

  public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
     //獲取包下面所有的類
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for(Class<?> type : typeSet){
      //排除匿名類纵寝、接口類论寨、成員類
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }
 public void registerAlias(Class<?> type) {
      //獲取類的簡單名稱
    String alias = type.getSimpleName();
      //查看是否有別名注解
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      //有別名注解,則別名為注解的值
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }
 public void registerAlias(String alias, Class<?> value) {
    //別名不可為空
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    //把別名小寫
    String key = alias.toLowerCase(Locale.ENGLISH);
    //configuration中記錄的別名如果存在當前別名爽茴,則拋出錯誤
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

pluginElement源碼解析

 private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      //獲取節(jié)點plugins下面的配置信息
      for (XNode child : parent.getChildren()) {
        //獲取插件類名稱
        String interceptor = child.getStringAttribute("interceptor");
        //獲取插件屬性配置
        Properties properties = child.getChildrenAsProperties();
        //插件都是實現(xiàn) org.apache.ibatis.plugin.Interceptor接口的類葬凳,把插件實例化
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //設置屬性
        interceptorInstance.setProperties(properties);
        //在configuration插件屬性中,增加插件
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

settingsElement源碼解析

private void settingsElement(XNode context) throws Exception {
    if (context != null) {
      //獲取所有setting配置參數(shù)信息<setting name="logImpl" value="LOG4J"/>
      Properties props = context.getChildrenAsProperties();
      // 確定所有的配置信息室奏,是configuration可以識別的
      MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
      for (Object key : props.keySet()) {
        if (!metaConfig.hasSetter(String.valueOf(key))) {
          throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
        }
      }
     /*指定 MyBatis 應如何自動映射列到字段或?qū)傩浴?     NONE 表示取消自動映射火焰;
     PARTIAL 只會自動映射沒有定義嵌套結果集映射的結果集。 
      FULL 會自動映射任意復雜的結果集(無論是否嵌套)     
      */
 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
//全局地開啟或關閉配置文件中的所有映射器已經(jīng)配置的任何緩存胧沫。     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
      //指定 Mybatis 創(chuàng)建具有延遲加載能力的對象所用到的代理工具昌简。
      configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
/*延遲加載的全局開關占业。當開啟時,所有關聯(lián)對象都會延遲加載纯赎。
 特定關聯(lián)關系中可通過設置fetchType屬性來覆蓋該項的開關狀態(tài)谦疾。
*/     
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
//當開啟時,任何方法的調(diào)用都會加載該對象的所有屬性犬金。否則念恍,每個屬性會按需加載     configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
//是否允許單一語句返回多結果集(需要兼容驅(qū)動)     configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
/*使用列標簽代替列名。不同的驅(qū)動在這方面會有不同的表現(xiàn)晚顷, 
具體可參考相關驅(qū)動文檔或通過測試這兩種不同的模式來觀察所用驅(qū)動的結果峰伙。
*/    
 configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
/*
允許 JDBC 支持自動生成主鍵,需要驅(qū)動兼容该默。 
如果設置為 true 則這個設置強制使用自動生成主鍵词爬,盡管一些驅(qū)動不能兼容但仍可正常工作(比如 Derby)
*/     configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
/*
配置默認的執(zhí)行器。
SIMPLE 就是普通的執(zhí)行器权均;
REUSE 執(zhí)行器會重用預處理語句(prepared statements)顿膨;
BATCH 執(zhí)行器將重用語句并執(zhí)行批量更新
*/     configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
  //設置超時時間,它決定驅(qū)動等待數(shù)據(jù)庫響應的秒數(shù)叽赊。 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
//為驅(qū)動的結果集獲取數(shù)量(fetchSize)設置一個提示值恋沃。此參數(shù)只可以在查詢設置中被覆蓋   
  configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
 /*
是否開啟自動駝峰命名規(guī)則(camel case)映射,即從經(jīng)典數(shù)據(jù)庫列名 A_COLUMN 到經(jīng)典 Java 屬性名 aColumn 的類似映射必指。
*/
 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
//允許在嵌套語句中使用分頁(RowBounds)囊咏。如果允許使用則設置為false     configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  /*
MyBatis 利用本地緩存機制(Local Cache)防止循環(huán)引用(circular references)和加速重復嵌套查詢。 
默認值為 SESSION塔橡,這種情況下會緩存一個會話中執(zhí)行的所有查詢梅割。 
若設置值為 STATEMENT,本地會話僅用在語句執(zhí)行上葛家,對相同 SqlSession 的不同調(diào)用將不會共享數(shù)據(jù)
*/  configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
 /*
當沒有為參數(shù)提供特定的 JDBC 類型時户辞,為空值指定 JDBC 類型。
 某些驅(qū)動需要指定列的 JDBC 類型癞谒,多數(shù)情況直接用一般類型即可底燎,
比如 NULL、VARCHAR 或 OTHER弹砚。
*/     configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
//  指定哪個對象的方法觸發(fā)一次延遲加載     configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  //允許在嵌套語句中使用分頁(ResultHandler)双仍。如果允許使用則設置為false    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
 //指定動態(tài) SQL 生成的默認語言。     configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
 /*
指定當結果集中值為 null 的時候是否調(diào)用映射對象的 setter(map 對象時為 put)方法桌吃,
這對于有 Map.keySet() 依賴或 null 值初始化的時候是有用的朱沃。
注意基本類型(int、boolean等)是不能設置成 null 的
*/     configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
  //指定 MyBatis 增加到日志名稱的前綴。
      configuration.setLogPrefix(props.getProperty("logPrefix"));
 // 指定 MyBatis 所用日志的具體實現(xiàn)逗物,未指定時將自動查找呕屎。
      configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
  /*
  指定一個提供Configuration實例的類。 
這個被返回的Configuration實例用來加載被反序列化對象的懶加載屬性值敬察。 
這個類必須包含一個簽名方法static Configuration getConfiguration(). (從 3.2.3 版本開始)
*/  configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    }
  }

environmentsElement源碼解析

 private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        //如果外部沒有傳入environment,則用default標簽指定的environment 
        environment = context.getStringAttribute("default");
      }
      //獲取所有的環(huán)境配置信息
      for (XNode child : context.getChildren()) {
        //后去當前節(jié)點尔当,環(huán)境序號ID
        String id = child.getStringAttribute("id");
        //看當前節(jié)點id序號是否和上面environment一致莲祸,如果environment為空,則拋出錯誤
        if (isSpecifiedEnvironment(id)) {
          //獲取事物管理配置信息 可以用別名椭迎,也可以指定類
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        //獲取連接池配置信息
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //根據(jù)配置信息锐帜,new出環(huán)境environment的builder類,注入環(huán)境序號畜号、事物管理缴阎、連接池信息
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
        //調(diào)用build()方法,返回Environment類简软,注入到configuration環(huán)境屬性中
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

databaseIdProviderElement源碼解析

 private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
/*配置案例
<databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql" />
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>
*/
    if (context != null) {
      String type = context.getStringAttribute("type");
      // DB_VENDOR為別名蛮拔,對應類
      //typeAliasRegistry.registerAlias("DB_VENDOR",VendorDatabaseIdProvider.class);
      if ("VENDOR".equals(type)) {
          type = "DB_VENDOR";
      }
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
   /*獲取databaseId,當databaseId不為空時,解析sql語句時痹升,會過濾sql語句中databaseId不匹配的配置信息
    案例 此sql配置的databaseId是oracle,當前environment的數(shù)據(jù)庫是mysql,則在讀取配置信息時建炫,會把這個sql配置信息過濾掉
 <insert id="insert" parameterType="com.study.entity.User" databaseId="oracle">
        insert into study_user (id,name,password,age,deleteFlag) values (#{id},#{name},#{password},#{age},#{deleteFlag})
    </insert>
    */
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
    //設置configuration的databaseId屬性信息
      configuration.setDatabaseId(databaseId);
    }
  }

typeHandlerElement源碼解析

 private void typeHandlerElement(XNode parent) throws Exception {
    //分析節(jié)點typeHandlers下的類型轉(zhuǎn)換器配置信息
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //通過掃描包,分析類型轉(zhuǎn)換器配置信息
          String typeHandlerPackage = child.getStringAttribute("name");
          //類型轉(zhuǎn)換器通過包名注冊 類型轉(zhuǎn)換器信息
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
           /*
            <typeHandlers>
               <typeHandler handler="" javaType="" jdbcType=""/>
               <package name=""/>
          </typeHandlers>
            */
          //獲取節(jié)點typeHandler 的javaType
          String javaTypeName = child.getStringAttribute("javaType");
          //獲取節(jié)點typeHandler 的jdbcType
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          //獲取節(jié)點typeHandler 的handler類
          String handlerTypeName = child.getStringAttribute("handler");
          //反射獲取javaType的class
          Class<?> javaTypeClass = resolveClass(javaTypeName);
           //通過JdbcType.valueOf(alias)疼蛾,獲取枚舉JdbcType
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          //通過反射獲取 類轉(zhuǎn)換的Class  接口TypeHandler的實現(xiàn)實例
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              //注冊類轉(zhuǎn)換信息
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
               //注冊類轉(zhuǎn)換信息
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
             //注冊類轉(zhuǎn)換信息
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
  }
  • TypeHandlerRegistry的屬性信息
  //jdbctype對應的typeHandler
  private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
//Type是所有類的始祖肛跌,javaTYpe對應所有的jdbcType的typeHandler
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
//未知的類轉(zhuǎn)換器
  private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
//class對應的類轉(zhuǎn)換器
  private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//查看是否有   MappedJdbcTypes注解
 MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
    if (mappedJdbcTypes != null) {
      for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
        //如果有注解則調(diào)用次方法,注冊類轉(zhuǎn)換器配置信息
        register(javaType, handledJdbcType, typeHandler);
      }
      if (mappedJdbcTypes.includeNullJdbcType()) {
        register(javaType, null, typeHandler);
      }
    } else {
      register(javaType, null, typeHandler);
    }
  }
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
  //查看是否已經(jīng)有javaType的類轉(zhuǎn)換配置信息
      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
      if (map == null) {
      //沒有則new出一個hashMap
        map = new HashMap<JdbcType, TypeHandler<?>>();
        TYPE_HANDLER_MAP.put(javaType, map);
      }
      map.put(jdbcType, handler);
    }
    //所有java類  類轉(zhuǎn)換信息 加入配置信息
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }

mapperElement源碼解析

 private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      //解析節(jié)點mappers 下的配置數(shù)據(jù)
      for (XNode child : parent.getChildren()) {
        //解析mppers下節(jié)點package,通過包掃描接口
        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) {
            ErrorContext.instance().resource(resource);
            //獲取mapper.xml的inputStream流
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //通過XMLMapperBuilder解析mapper.xml配置信息        SqlFragments為靜態(tài)sql語句
            /*
        <sql id="studentProperties"><!--sql片段-->
        select 
            stud_id as studId
            , name, email
            , dob
            , phone
        from students
        </sql>
            可以通過<include refid="studentProperties"></include><!--復用-->c
            */      
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } 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.");
          }
        }
      }
    }
  }
  • 掃描包mapper解析源碼分析
 public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }
  • mapperRegistry類屬性
//全局configuration
private final Configuration config;
//接口  對應的代理類工廠   和spring集成需要用到這個  
 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
  public <T> void addMapper(Class<T> type) {
    //解析接口類
    if (type.isInterface()) {
      //接口類如果已經(jīng)解析過察郁,則拋出錯誤
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
      //在knownMappers中放入接口類衍慎,接口類代理工廠的信息
        knownMappers.put(type, new MapperProxyFactory<T>(type));
         //new 出MapperAnnotationBuilder注解解析器
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
  • 通過 MapperAnnotationBuilder進行解析
 public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
    String resource = type.getName().replace('.', '/') + ".java (best guess)";
    this.assistant = new MapperBuilderAssistant(configuration, resource);
    this.configuration = configuration;
    this.type = type;
    //sqlAnnotationTypes注解
    sqlAnnotationTypes.add(Select.class);
    sqlAnnotationTypes.add(Insert.class);
    sqlAnnotationTypes.add(Update.class);
    sqlAnnotationTypes.add(Delete.class);
     //sqlProviderAnnotationTypes注解  兩種注解不可以同時存在
    sqlProviderAnnotationTypes.add(SelectProvider.class);
    sqlProviderAnnotationTypes.add(InsertProvider.class);
    sqlProviderAnnotationTypes.add(UpdateProvider.class);
    sqlProviderAnnotationTypes.add(DeleteProvider.class);
  }

  public void parse() {
    String resource = type.toString();
    //查看是否已經(jīng)加載過
    if (!configuration.isResourceLoaded(resource)) {
      //查找下面的xml文件,接口同名以'.xml'結尾
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      //根據(jù)方法注解皮钠,來解析mapper接口
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          //過濾掉橋接方法
          if (!method.isBridge()) {
            //解析方法稳捆,如果當前命名空間+method名稱已經(jīng)解析過,則拋出錯誤
            /*Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
              重寫了put方法麦轰,會判斷是否key已經(jīng)存在眷柔,存在會拋出錯誤
            */
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }
  • XMLMapperBuilder解析xml過程解析
public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //解析xml文件下mapper節(jié)點下的配置信息
      configurationElement(parser.evalNode("/mapper"));
      //記錄該資源已經(jīng)解析過
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }
 private void configurationElement(XNode context) {
    try {
       //獲取命名空間名稱
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //解析助理 設置當前命名空間名稱
      builderAssistant.setCurrentNamespace(namespace);
       //緩存配置解析
      cacheRefElement(context.evalNode("cache-ref"));
       //緩存配置解析
      cacheElement(context.evalNode("cache"));
      //不建議使用
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //resultMap解析
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //sql解析
      sqlElement(context.evalNodes("/mapper/sql"));
      //增刪改查sql語句解析
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

?? Mybatis初始化過程中,解析parameterMap原朝、resultMap驯嘱、"select|insert|update|delete"元素,無疑是重頭戲
??元素parameterMap將會解析為ParameterMap對象喳坠,該對象包含一個List<ParameterMapping>集合鞠评,是one-to-many關系。
?? 元素resultMap將會解析為ResultMap對象壕鹉,該對象包含一個List<ResultMapping>集合剃幌,是one-to-many關系聋涨。
??元素"select|insert|update|delete"將會被解析為MappedStatement對象,該對象包含了ParameterMap负乡、ResultMap等對象

  • resultMap解析
    • ResultMap 類屬性
  //resultMap的id值
  private String id;
  //對應的java類
  private Class<?> type;
   //所有的resultMapping對象牍白,包括constructor/idArg,constructor/arg,result,association,collection,
  //但不包括association和collection里的子節(jié)點
  private List<ResultMapping> resultMappings;
  //包括constructor/idArg,id
  private List<ResultMapping> idResultMappings;
  //constructor里的子節(jié)點
  private List<ResultMapping> constructorResultMappings;
  //除constructor里的子節(jié)點,其他都是,result,association,collection,id 
  private List<ResultMapping> propertyResultMappings;
  //所有被映射的列 
  private Set<String> mappedColumns;
  //通過case來判斷映射類抖棘,比較少用 
  private Discriminator discriminator;
   //是否有內(nèi)映射茂腥,association, collection都為內(nèi)映射,
  //內(nèi)查詢不算(就是的reulst節(jié)點中配置select屬性的情況)
  private boolean hasNestedResultMaps;
  //是否有查詢,  
  private boolean hasNestedQueries;
  //是否要求自動映射  
  private Boolean autoMapping;
    • ResultMap.Builder.build()方法
 public ResultMap build() {
      //id不可為空
      if (resultMap.id == null) {
        throw new IllegalArgumentException("ResultMaps must have an id");
      }
      //映射的列
      resultMap.mappedColumns = new HashSet<String>();
      //包括constructor/idArg,id
      resultMap.idResultMappings = new ArrayList<ResultMapping>();
      //constructor里的子節(jié)點
      resultMap.constructorResultMappings = new ArrayList<ResultMapping>();
      //除constructor里的子節(jié)點,其他都是
      resultMap.propertyResultMappings = new ArrayList<ResultMapping>();
      for (ResultMapping resultMapping : resultMap.resultMappings) {
        //如果其中一個resultMapping有內(nèi)查詢切省,則這個resultMap也就是有內(nèi)查詢
        resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
        //如果其中一個resultMapping有內(nèi)映射最岗,則這個resultMap也就是有內(nèi)映射 
        resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null);
        //獲取字段名稱
        final String column = resultMapping.getColumn();
        if (column != null) {
          resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
        } else if (resultMapping.isCompositeResult()) {
        //組合配置分析
          for (ResultMapping compositeResultMapping : resultMapping.getComposites()) {
            final String compositeColumn = compositeResultMapping.getColumn();
            if (compositeColumn != null) {
              resultMap.mappedColumns.add(compositeColumn.toUpperCase(Locale.ENGLISH));
            }
          }
        }
        //如果是構造器resultMapping,則放入constructorResultMappings
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          resultMap.constructorResultMappings.add(resultMapping);
        } else {
      //其余的放入propertyResultMappings
          resultMap.propertyResultMappings.add(resultMapping);
        }
         //ID的類型resultMapping放入idResultMappings
        if (resultMapping.getFlags().contains(ResultFlag.ID)) {
          resultMap.idResultMappings.add(resultMapping);
        }
      }
      //如果沒有指定ID,idResultMappings包含所有的resultMapping
      if (resultMap.idResultMappings.isEmpty()) {
        resultMap.idResultMappings.addAll(resultMap.resultMappings);
      }
      //集合為只讀的
      resultMap.resultMappings = Collections.unmodifiableList(resultMap.resultMappings);
      resultMap.idResultMappings = Collections.unmodifiableList(resultMap.idResultMappings);
      resultMap.constructorResultMappings = Collections.unmodifiableList(resultMap.constructorResultMappings);
      resultMap.propertyResultMappings = Collections.unmodifiableList(resultMap.propertyResultMappings);
      resultMap.mappedColumns = Collections.unmodifiableSet(resultMap.mappedColumns);
      return resultMap;
    }
  • 增刪改查sql語句解析
    • 一個MappedStatement對象對應Mapper配置文件中的一個select/update/insert/delete節(jié)點朝捆,主要描述的是一條SQL語句
/*
 * MappedStatement所有的屬性方法
*/

  private String resource;
  private Configuration configuration;
  //節(jié)點中的id屬性加要命名空間 
  private String id;
  private Integer fetchSize;
  //超時時間
  private Integer timeout;
  //statement類型 STATEMENT, PREPARED, CALLABLE
  private StatementType statementType;
  //結果集類型 
  private ResultSetType resultSetType;
  //對應一條SQL語句 動態(tài)sql語句封裝在這里
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  //是否啟用緩存
  private boolean useCache;
  private boolean resultOrdered;
  //SQL的類型,select/update/insert/detete  
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  //是否有內(nèi)映射  
  private boolean hasNestedResultMaps;
  //數(shù)據(jù)庫ID
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
 public void parseStatementNode() {
      //獲取id屬性
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // 解析selectKey
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // 解析sql語句
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }
    • SqlSource 解析
      • SqlSource接口
public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

public class BoundSql {
  //解析后的最終sql語句般渡,可能帶有問號 "?"
  private String sql;
  //參數(shù)
  private List<ParameterMapping> 
  parameterMappings;
  private Object parameterObject;
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;
}
public SqlSource parseScriptNode() {
    //解析sql語句,把節(jié)點下的sql語句信息芙盘,封裝到SqlNode中
    List<SqlNode> contents = parseDynamicTags(context);
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    //是否是動態(tài)
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
    //遞歸調(diào)用此方法
  List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        //是否是動態(tài) ${}為動態(tài)
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } 
//含有set if where trim等節(jié)點 
else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { 
        String nodeName = child.getNode().getNodeName();
   //獲取對應的nodeHandler驯用,所有類型的nodeHandler看下面的代碼      
  NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        //解析此節(jié)點,會遞歸調(diào)用當前方法
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return contents;
  }
  //所有nodeHandler類型
  NodeHandler nodeHandlers(String nodeName) {
    Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
    map.put("trim", new TrimHandler());
    map.put("where", new WhereHandler());
    map.put("set", new SetHandler());
    map.put("foreach", new ForEachHandler());
    map.put("if", new IfHandler());
    map.put("choose", new ChooseHandler());
    map.put("when", new IfHandler());
    map.put("otherwise", new OtherwiseHandler());
    map.put("bind", new BindHandler());
    return map.get(nodeName);
  }
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末儒老,一起剝皮案震驚了整個濱河市晨汹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贷盲,老刑警劉巖淘这,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巩剖,居然都是意外死亡铝穷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門佳魔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曙聂,“玉大人,你說我怎么就攤上這事鞠鲜∧梗” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵贤姆,是天一觀的道長榆苞。 經(jīng)常有香客問我,道長霞捡,這世上最難降的妖魔是什么坐漏? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上赊琳,老公的妹妹穿的比我還像新娘街夭。我一直安慰自己,他們只是感情好躏筏,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布板丽。 她就那樣靜靜地躺著,像睡著了一般趁尼。 火紅的嫁衣襯著肌膚如雪埃碱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天弱卡,我揣著相機與錄音,去河邊找鬼住册。 笑死婶博,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的荧飞。 我是一名探鬼主播凡人,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叹阔!你這毒婦竟也來了挠轴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤耳幢,失蹤者是張志新(化名)和其女友劉穎岸晦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體睛藻,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡启上,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了店印。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冈在。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖按摘,靈堂內(nèi)的尸體忽然破棺而出包券,到底是詐尸還是另有隱情,我是刑警寧澤炫贤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布溅固,位于F島的核電站,受9級特大地震影響兰珍,放射性物質(zhì)發(fā)生泄漏发魄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望励幼。 院中可真熱鬧汰寓,春花似錦、人聲如沸苹粟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嵌削。三九已至毛好,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苛秕,已是汗流浹背肌访。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艇劫,地道東北人吼驶。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像店煞,于是被迫代替她去往敵國和親蟹演。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容