从源码看世界:Mybatis初始化过程(上)

前文跟大家过了一遍Mybatis的执行过程,但是仍然有不少疑问点未解释清楚,例如具体sql到底什么时候生成,mapper接口是如何实例化,为何executor用的是SimpleExecutor等等。这次和大家一起看看Mybatis是如何初始化,相信以上问题将会一一解开。

 

先回顾SqlSessionFactory的初始化过程:

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,它在初始化的时候其实已经注册了不少类型别名:

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进行解析:

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

首先创建Mapper代理工厂类,用于实例化Mapper;然后parser.parse()进行Mapper解析:

  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内生成:

void parseStatement(Method method) {
  //获取参数类型,有多个时为ParamMap(HashMap子类)
  Class<?> parameterTypeClass = getParameterType(method);
  //获取动态语言驱动,默认使用XMLLanguageDriver
  LanguageDriver languageDriver = getLanguageDriver(method);
  //sql生成源,四种实现
  SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
  // ……
}

根据method的注解内容,生成对应的SqlSource实现类对象:

up-ce94c355fd2b70a8110c72da7d19c5227fa.png

  • DynamicSqlSource:动态sql生成源,即需要动态变量替换来动态生成sql
  • RawSqlSource:静态sql生成源,实际上包装了StaticSqlSource
  • ProviderSqlSource:自定义sql生成源,方便自己拼接sql
  • StaticSqlSource:内部使用的静态sql生成源,DynamicSqlSource和RawSqlSource都通过其返回sql,但DynamicSqlSource是每次都生成新的对象(动态)

至此,sql的生成已完成,具体的sql解析请自行查看源码,主要还是字符串的解析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值