從源碼看世界:Mybatis初始化過程(上)

前文跟你們過了一遍Mybatis的執行過程,可是仍然有很多疑問點未解釋清楚,例如具體sql到底何時生成,mapper接口是如何實例化,爲什麼executor用的是SimpleExecutor等等。此次和你們一塊兒看看Mybatis是如何初始化,相信以上問題將會一一解開。java

先回顧SqlSessionFactory的初始化過程:spring

TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("dev", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(StudentMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
  1. 首先建立事務管理工廠TransactionFactory實例,事務管理支持兩種方式:JDBC事務管理(JdbcTransaction)和WEB容器事務管理(ManagedTransactionFactory),通常經常使用的是JdbcTransaction,其實Mybatis的事務管理機制並無作過多的操做,只是在外面套了一層方便調用而已。另外若是與spring一塊兒使用時,spring提供了一個實現類SpringManagedTransaction,它其實也是經過使用JDBC來進行事務管理的,有興趣的同窗可自行了解。
  2. 其次建立環境實例,綁定事務管理工廠和數據源。
  3. 初始化全局配置類Configuration,除了綁定環境實例,還註冊了類型別名:
public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

先來看看TypeAliasRegistry,它在初始化的時候其實已經註冊了很多類型別名:sql

public TypeAliasRegistry() {
    //字符串類型
    registerAlias("string", String.class);
    //基本包裝類型
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);
    //基本數組包裝類型
    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);
    //基本類型
    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);
    //基本數組類型
    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);
    //日期數字型
    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);
    //集合型
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);
    //結果集型
    registerAlias("ResultSet", ResultSet.class);
  }

registerAlias方法最終是保存在HashMap裏,key=別名,value=類對象。數組

從註冊的內容中能夠看出,類型別名主要用於取代複雜的類型全限定名,因爲Mybatis支持xml和註解配置,配置通常以字符串形式鍵入,使用類型別名能夠更加方便地進入配置,其用途或用於映射器配置文件中進行參數類型與返回結果類型的設置,或用於其它特定字符的類解析例如JDBC=JdbcTransactionFactory等等。緩存

4. 註冊具體Mapper,利用MapperRegistry對Mapper進行解析:app

knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();

首先建立Mapper代理工廠類,用於實例化Mapper;而後parser.parse()進行Mapper解析:ide

public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      //讀取並解析namespace的xml配置文件
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      //解析二級緩存註解,下同,Ref用於多個mapper之間共享緩存
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            //解析方法,生成MappedStatement
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    //解析在parseStatement拋異常的方法
    parsePendingMethods();
  }

前文說過MappedStatement主要是記錄sql、輸入參數、輸出結果類型等信息,所以sql確定在parseStatement內生成:ui

void parseStatement(Method method) {
  //獲取參數類型,有多個時爲ParamMap(HashMap子類)
  Class<?> parameterTypeClass = getParameterType(method);
  //獲取動態語言驅動,默認使用XMLLanguageDriver
  LanguageDriver languageDriver = getLanguageDriver(method);
  //sql生成源,四種實現
  SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
  // ……
}

根據method的註解內容,生成對應的SqlSource實現類對象:this

  • DynamicSqlSource:動態sql生成源,即須要動態變量替換來動態生成sql
  • RawSqlSource:靜態sql生成源,實際上包裝了StaticSqlSource
  • ProviderSqlSource:自定義sql生成源,方便本身拼接sql
  • StaticSqlSource:內部使用的靜態sql生成源,DynamicSqlSource和RawSqlSource都經過其返回sql,但DynamicSqlSource是每次都生成新的對象(動態)

至此,sql的生成已完成,具體的sql解析請自行查看源碼,主要仍是字符串的解析。spa

相關文章
相關標籤/搜索