Mybatis源码解析-Mybatis初始化过程

测试方法

public class UserMapperTest {

    private UserMapper userMapper;

    SqlSession sqlSession = null;
    SqlSessionFactory sqlSessionFactory = null;

    @Before
    public void setUp() throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //openSession(boolean autoCommit) 设置事务是否自动提交
        sqlSession = sqlSessionFactory.openSession(true);
        // 获取动态代理实现类
        this.userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void testQueryUserById() {
        User user = userMapper.queryUserById(1L);
        System.out.println(user);
    }

    @Test
    public void testQueryAll() {
        List<User> users = userMapper.queryAll();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

SqlSessionFactoryBuilder 的 build 方法提供了对Reader和 InputStream 两种类型的支持,并提供了Resources类简化配置文件的读取。

public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder() {
    }

    public SqlSessionFactory build(Reader reader) {
        return this.build((Reader)reader, (String)null, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, String environment) {
        return this.build((Reader)reader, environment, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, Properties properties) {
        return this.build((Reader)reader, (String)null, properties);
    }

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                reader.close();
            } catch (IOException var13) {
                ;
            }

        }

        return var5;
    }

    public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment) {
        return this.build((InputStream)inputStream, environment, (Properties)null);
    }

    public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return this.build((InputStream)inputStream, (String)null, properties);
    }
    //通过XMLConfigBuilder解析配置文件构建SqlSessionFactory对象
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
                ;
            }

        }

        return var5;
    }

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

解析过程

// XMLConfigBuilder
public Configuration parse() {
    if (this.parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    } else {
        this.parsed = true;
        // 解析mybatis配置文件根结点configuration
        this.parseConfiguration(this.parser.evalNode("/configuration"));
        return this.configuration;
    }
}
// 解析mybatis核心配置文件的子节点信息(properties、settings、typeAliases、plugins、mappers...)
private void parseConfiguration(XNode root) {
    try {
        // 解析properties节点
        this.propertiesElement(root.evalNode("properties"));
        // 解析settings节点
        Properties settings = this.settingsAsProperties(root.evalNode("settings"));
        this.loadCustomVfs(settings);
        // 解析typeAliases类型别名节点
        this.typeAliasesElement(root.evalNode("typeAliases"));
        // 解析plugin节点
        this.pluginElement(root.evalNode("plugins"));
        // 解析objectFactory节点
        this.objectFactoryElement(root.evalNode("objectFactory"));
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        // 解析reflectorFactory节点
        this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
        this.settingsElement(settings);
        // 解析environments节点
        this.environmentsElement(root.evalNode("environments"));
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        this.typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析mappers节点
        this.mapperElement(root.evalNode("mappers"));
    } catch (Exception var3) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    }
}

properties解析

可通过resource或url属性引用外部资源文件(两者不能同时存在),或使用property子元素配置

<!-- 引入外部资源配置文件 -->
<properties resource="jdbc.properties"/>

解析properties配置

// XMLConfigBuilder
private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
        Properties defaults = context.getChildrenAsProperties(); // 获取所有的property子节点
        String resource = context.getStringAttribute("resource"); // 获取resource属性值
        String url = context.getStringAttribute("url"); // 获取url属性值
        if (resource != null && url != null) { //获取resource及url属性值 两者不能同时存在
            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) {
            defaults.putAll(Resources.getResourceAsProperties(resource)); // 将resource对应配置信息添加到defaults容器中
        } else if (url != null) {
            defaults.putAll(Resources.getUrlAsProperties(url)); // 将url对应配置信息添加到defaults容器中
        }
        //获取全局配置文件Configuration已有变量 并添加至defaults容器中
        Properties vars = this.configuration.getVariables();
        if (vars != null) {
            defaults.putAll(vars); 
        }

        this.parser.setVariables(defaults);
        this.configuration.setVariables(defaults); // 将proprties配置解析得到参数保存至全局配置类configuration的属性variables中
    }

}

解析过程

  • 首先解析properties的子节点property元素,将属性存入defaults容器。
  • 然后解析properties上面的resource或url属性(两者不能同时存在),解析外部配置文件并存入defaults容器。
  • 将defaults容器中所有配置信息添加到全局配置对象configuration中。

摘自mybatis官网

如果属性在不只一个地方进行了配置,那么MyBatis将按照下面的顺序来加载:
• 在 properties 元素体内指定的属性首先被读取。
• 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url
• 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。

typeAliases解析

可单独针对某个类型配置别名,也可以配置扫描包自动生成改包下所有类的别名(默认生成Bean首字母小写别名)

<typeAliases>
    <!-- 别名 type 指定java对象类型 alias 别名名称 -->
     <typeAlias type="com.laravelshao.learning.mybatis.pojo.User" alias="User"/>
</typeAliases>
<typeAliases>
    <!-- 指定扫描包 Mybatis会将该包下所有类都生成别名(Bean首字母小写别名) -->
    <package name="com.laravelshao.learning.mybatis.pojo"/>
</typeAliases>

解析typeAliases配置

// XMLConfigBuilder
private void typeAliasesElement(XNode parent) {
    if (parent != null) {
        Iterator var2 = parent.getChildren().iterator();

        while(var2.hasNext()) {
            XNode child = (XNode)var2.next();
            String alias;
            if ("package".equals(child.getName())) {
                alias = child.getStringAttribute("name");
                this.configuration.getTypeAliasRegistry().registerAliases(alias); //注册指定包下所有类
            } else {
                alias = child.getStringAttribute("alias");
                String type = child.getStringAttribute("type");

                try {
                    Class<?> clazz = Resources.classForName(type);
                    if (alias == null) {
                        this.typeAliasRegistry.registerAlias(clazz);
                    } else {
                        this.typeAliasRegistry.registerAlias(alias, clazz); //指定单独类的别名
                    }
                } catch (ClassNotFoundException var7) {
                    throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + var7, var7);
                }
            }
        }
    }

}

注册类型别名

// TypeAliasRegistry
public void registerAliases(String packageName) { // 注册指定包的类型别名
    this.registerAliases(packageName, Object.class);
}

public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
    resolverUtil.find(new IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    Iterator var5 = typeSet.iterator();

    while(var5.hasNext()) {
        Class<?> type = (Class)var5.next();
        if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
            this.registerAlias(type);
        }
    }

}

public void registerAlias(String alias, Class<?> value) { // 注册指定类的别名
    if (alias == null) {
        throw new TypeException("The parameter alias cannot be null");
    } else {
        String key = alias.toLowerCase(Locale.ENGLISH);
        if (this.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
            throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
        } else {
            this.TYPE_ALIASES.put(key, value);
        }
    }
}

plugins解析

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
• Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
• ParameterHandler (getParameterObject, setParameters)
• ResultSetHandler (handleResultSets, handleOutputParameters)
• StatementHandler (prepare, parameterize, batch, update, query)

分页插件PageHelper配置

<!-- 配置分页插件 PageHelper -->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!-- 设置数据库方言 -->
        <property name="dialect" value="mysql"/>
        <!-- 使用RowBounds分页是否进行count查询 默认false-->
        <property name="rowBoundsWithCount" value="true"/>
    </plugin>
</plugins>

解析plugins配置

// XMLConfigBuilder
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
        Iterator var2 = parent.getChildren().iterator();

        while(var2.hasNext()) {
            XNode child = (XNode)var2.next();
            String interceptor = child.getStringAttribute("interceptor"); // 获取interceptor属性值
            Properties properties = child.getChildrenAsProperties();
            Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance(); // 创建Interceptor实例
            interceptorInstance.setProperties(properties); // 设置属性
            this.configuration.addInterceptor(interceptorInstance); //添加至Configuration的interceptorChain中
        }
    }

}

settings解析

可以设置mybatis运行时行为,包括设置二级缓存、延迟加载、按需加载、经典命名到驼峰命名映射等(下面为常用属性)。

设置参数 描述 有效值 默认值
cacheEnabled 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 true|false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。 true|false false
aggressiveLazyLoading 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods). true|false false(true in ≤3.4.1)
mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 true|false false

Settings解析并添加至全局配置类Configuration

// XMLConfigBuilder
private Properties settingsAsProperties(XNode context) {
    if (context == null) {
        return new Properties();
    } else {
        Properties props = context.getChildrenAsProperties();
        MetaClass metaConfig = MetaClass.forClass(Configuration.class, this.localReflectorFactory);
        Iterator var4 = props.keySet().iterator();

        Object key;
        do {
            if (!var4.hasNext()) {
                return props;
            }

            key = var4.next();
        } while(metaConfig.hasSetter(String.valueOf(key)));

        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
    }
}

private void settingsElement(Properties props) throws Exception {
    this.configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    this.configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    this.configuration.setCacheEnabled(this.booleanValueOf(props.getProperty("cacheEnabled"), true));
    this.configuration.setProxyFactory((ProxyFactory)this.createInstance(props.getProperty("proxyFactory")));
    this.configuration.setLazyLoadingEnabled(this.booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); 
    this.configuration.setAggressiveLazyLoading(this.booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    this.configuration.setMultipleResultSetsEnabled(this.booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    this.configuration.setUseColumnLabel(this.booleanValueOf(props.getProperty("useColumnLabel"), true));
    this.configuration.setUseGeneratedKeys(this.booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    this.configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    this.configuration.setDefaultStatementTimeout(this.integerValueOf(props.getProperty("defaultStatementTimeout"), (Integer)null));
    this.configuration.setDefaultFetchSize(this.integerValueOf(props.getProperty("defaultFetchSize"), (Integer)null));
    this.configuration.setMapUnderscoreToCamelCase(this.booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    this.configuration.setSafeRowBoundsEnabled(this.booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    this.configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    this.configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    this.configuration.setLazyLoadTriggerMethods(this.stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    this.configuration.setSafeResultHandlerEnabled(this.booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    this.configuration.setDefaultScriptingLanguage(this.resolveClass(props.getProperty("defaultScriptingLanguage")));
    Class<? extends TypeHandler> typeHandler = this.resolveClass(props.getProperty("defaultEnumTypeHandler"));
    this.configuration.setDefaultEnumTypeHandler(typeHandler);
    this.configuration.setCallSettersOnNulls(this.booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    this.configuration.setUseActualParamName(this.booleanValueOf(props.getProperty("useActualParamName"), true));
    this.configuration.setReturnInstanceForEmptyRow(this.booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    this.configuration.setLogPrefix(props.getProperty("logPrefix"));
    Class<? extends Log> logImpl = this.resolveClass(props.getProperty("logImpl"));
    this.configuration.setLogImpl(logImpl);
    this.configuration.setConfigurationFactory(this.resolveClass(props.getProperty("configurationFactory")));
}

mappers解析

mappers定义了mapper映射文件位置,可以使用类路径、url、全局类名或配置扫描包来引入。

<mappers>
    <!-- 使用相对于类路径的资源引用 -->
    <mapper resource="com/laravelshao/learning/mybatis/mapper/UserMapper.xml"/>
    <!-- 使用完全限定资源定位符(URL) -->
    <mapper url="file:///var/mappers/UserMapper.xml"/>
    <!-- 使用映射器接口实现类的完全限定类名 -->
    <mapper class="com.laravelshao.learning.mybatis.mapper.UserMapper"/>
    <!-- 将包内的映射器接口实现全部注册为映射器 -->
    <package name="com.laravelshao.learning.mybatis.mapper"/>
</mappers>

mapper解析流程

// XMLConfigBuilder
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        Iterator var2 = parent.getChildren().iterator();

        while(true) {
            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String resource;
                if ("package".equals(child.getName())) { // 如果是package子节点
                    resource = child.getStringAttribute("name"); // 获取package节点的name属性值(mapper class的包路径)
                    this.configuration.addMappers(resource); // 将所有的mapper class添加到configuration的mapperRegistry容器中
                } else {
            // mapper子节点 获取resource、url、class属性值
                    resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    XMLMapperBuilder mapperParser;
                    InputStream inputStream;
                    if (resource != null && url == null && mapperClass == null) { // 配置了resource属性
                        ErrorContext.instance().resource(resource);
                        inputStream = Resources.getResourceAsStream(resource);
            // 使用XMLMapperBuilder解析mapper配置文件 并注册到configuration的mapperRegistry容器中
                        mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) { // 配置了url属性
                        ErrorContext.instance().resource(url);
                        inputStream = Resources.getUrlAsStream(url);
                        mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                        mapperParser.parse();
                    } else { // 配置了class属性
                        if (resource != null || url != null || mapperClass == null) {
                            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                        }

                        Class<?> mapperInterface = Resources.classForName(mapperClass); // 将全限定名转换为Class对象
                        this.configuration.addMapper(mapperInterface); // 注册到configuration的mapperRegistry容器中
                    }
                }
            }

            return;
        }
    }
}
// XMLMapperBuilder
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration); // 将configuration赋值给BaseBuilder维护的全局configuration 
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource); // 创建MapperBuilderAssistant对象
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
}

// 解析mapper文件
public void parse() {
    if (!this.configuration.isResourceLoaded(this.resource)) { // 若当前mapper还未加载 则执行解析
        this.configurationElement(this.parser.evalNode("/mapper")); // 解析mapper配置元素
        this.configuration.addLoadedResource(this.resource); // 添加到configuration的loadedResources容器
        this.bindMapperForNamespace(); // 将当前mapper文件注册到configuration的mapperRegistry容器中
    }

    this.parsePendingResultMaps();
    this.parsePendingCacheRefs();
    this.parsePendingStatements();
}

configurationElement方法

// XMLMapperBuilder
private void configurationElement(XNode context) {
    try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace != null && !namespace.equals("")) {
            this.builderAssistant.setCurrentNamespace(namespace); // 将namespace设置到MapperBuilderAssistant中
            this.cacheRefElement(context.evalNode("cache-ref")); // 解析cache-ref节点
            this.cacheElement(context.evalNode("cache")); // 解析cache节点
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析parameterMap节点
            this.resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析resultMap
            this.sqlElement(context.evalNodes("/mapper/sql")); // 解析sql节点
            this.buildStatementFromContext(context.evalNodes("select|insert|update|delete")); // 解析具体sql节点
        } else {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
    } catch (Exception var3) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
    }
}
// XMLMapperBuilder
private void resultMapElements(List<XNode> list) throws Exception {
    Iterator var2 = list.iterator();

    while(var2.hasNext()) {
        XNode resultMapNode = (XNode)var2.next();

        try {
            this.resultMapElement(resultMapNode);
        } catch (IncompleteElementException var5) {
            ;
        }
    }

}

private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
    return this.resultMapElement(resultMapNode, Collections.emptyList());
}
// 解析resultMap节点
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); // 获取resultMap节点的id属性
    // 获取resultMap节点的type属性
    String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType"))));
    String extend = resultMapNode.getStringAttribute("extends"); // 获取resultMap节点的extends属性
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); // 获取resultMap节点的autoMapping属性
    Class<?> typeClass = this.resolveClass(type); // 将返回值类型转换为Class对象
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList(); 
    resultMappings.addAll(additionalResultMappings); // 添加到resultMappings集合(存储resultMap的子节点)
    List<XNode> resultChildren = resultMapNode.getChildren();
    Iterator var11 = resultChildren.iterator();

    while(var11.hasNext()) {
        XNode resultChild = (XNode)var11.next();
        if ("constructor".equals(resultChild.getName())) { // 解析constructor节点的子节点并添加resultMappings集合中
            this.processConstructorElement(resultChild, typeClass, resultMappings);
        } else if ("discriminator".equals(resultChild.getName())) { // 解析discriminator节点的子节点并添加resultMappings集合中
            discriminator = this.processDiscriminatorElement(resultChild, typeClass, resultMappings);
        } else { 
            List<ResultFlag> flags = new ArrayList();
            if ("id".equals(resultChild.getName())) {
                flags.add(ResultFlag.ID);
            }

            resultMappings.add(this.buildResultMappingFromContext(resultChild, typeClass, flags));
        }
    }

    ResultMapResolver resultMapResolver = new ResultMapResolver(this.builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);

    try {
        return resultMapResolver.resolve();
    } catch (IncompleteElementException var14) {
        this.configuration.addIncompleteResultMap(resultMapResolver);
        throw var14;
    }
}

将sql语句解析为MappedStatement对象,并添加到全局configuration中

// XMLMapperBuilder
private void buildStatementFromContext(List<XNode> list) {
    if (this.configuration.getDatabaseId() != null) {
        this.buildStatementFromContext(list, this.configuration.getDatabaseId());
    }

    this.buildStatementFromContext(list, (String)null);
}

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    Iterator var3 = list.iterator();

    while(var3.hasNext()) {
        XNode context = (XNode)var3.next();
        XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);

        try {
            statementParser.parseStatementNode();
        } catch (IncompleteElementException var7) {
            this.configuration.addIncompleteStatement(statementParser);
        }
    }

}
// XMLStatementBuilder
public void parseStatementNode() {
    String id = this.context.getStringAttribute("id");
    String databaseId = this.context.getStringAttribute("databaseId");
    if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        Integer fetchSize = this.context.getIntAttribute("fetchSize");
        Integer timeout = this.context.getIntAttribute("timeout");
        String parameterMap = this.context.getStringAttribute("parameterMap");
        String parameterType = this.context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = this.resolveClass(parameterType);
        String resultMap = this.context.getStringAttribute("resultMap");
        String resultType = this.context.getStringAttribute("resultType");
        String lang = this.context.getStringAttribute("lang");
        LanguageDriver langDriver = this.getLanguageDriver(lang);
        Class<?> resultTypeClass = this.resolveClass(resultType);
        String resultSetType = this.context.getStringAttribute("resultSetType");
        StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
        String nodeName = this.context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
        boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
        includeParser.applyIncludes(this.context.getNode());
        this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
        SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
        String resultSets = this.context.getStringAttribute("resultSets");
        String keyProperty = this.context.getStringAttribute("keyProperty");
        String keyColumn = this.context.getStringAttribute("keyColumn");
        String keyStatementId = id + "!selectKey";
        keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
        Object keyGenerator;
        if (this.configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
        } else {
            keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
        // 使用MapperBuilderAssistant辅助类添加MappedStatement
        this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}

解析完成后会返回configuration对象,通过build(configuration)方法创建一个DefaultSqlSessionFactory

// SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
            ;
        }

    }

    return var5;
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config); // 创建默认实现DefaultSqlSessionFactory
}

自此,mybatis的初始化过程完成。

参考资料

http://www.mybatis.org/mybatis-3/zh/configuration.html
https://www.jianshu.com/p/7bc6d3b7fb45
http://www.cnblogs.com/dongying/tag/Mybatis%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA%E7%B3%BB%E5%88%97/

猜你喜欢

转载自blog.csdn.net/laravelshao/article/details/81191795