前文跟你们过了一遍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);
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
至此,sql的生成已完成,具体的sql解析请自行查看源码,主要仍是字符串的解析。spa