(二) mybatis 源码之 SqlSessionFactory

从 mybatis 的官网,写最简单的 “ HelloWorld ”


Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = factory.openSession();
StudentDao mapper = session.getMapper(StudentDao.class);
System.err.println(mapper.listStudent());

首先解析全局配置文件, 转换成一个 Reader 对象, 这个是 JDK 的代码,直接看第二行, 创建了一个 SqlSessionFactory 对象

SqlSesseionFactory

使用了 建造者模式,, 最后调用的是 build 方法,创建了一个 SqlSessionFactory 对象

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        // 将全局配置文件转换成一个 Document 对象放入 XPathParser 对象中
        // 而 XPathParser 对象保存在此 XMLConfiguration 对象中
        // 这个 Document 对象类似于 “树” 结构
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        // 开始解析这个 Document 对象, 保存为一个 Configuration 对象中
        // 创建默认的 SqlSessionFactory 对象, DefaultSqlSessionFactory
        return build(parser.parse());
    }
   ....
}

解析全局配置文件

// 由 XMLConfigBuilder 对象解析

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    // 解析完毕后设置一个标志位, 只能解析配置文件一次
    parsed = true;
    // 解析配置文件中的 <configuration> 标签, 保存到 XNode 对象中
    // 然后处理这个 XNode 对象
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

解析 <configuration> 下的所有子标签

// 挨个解析所有子标签, 可以看到, 严格按照顺序依次执行
private void parseConfiguration(XNode root) {
    try {
        propertiesElement(root.evalNode("properties"));
        // 将 <setting> 标签的元素保存为 properties 对象
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        // 日志
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // 解析并创建数据源
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析 mapper 配置文件
        mapperElement(root.evalNode("mappers"));
    }
    catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }

这里就不一个一个的看了, 看看最常用的 <environments><mappers> ……

解析 <environments> 标签

// 处理 <environments> 标签, 这些信息被保存在 XNode 对象中,作为参数传了进来
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        // 如果没有使用指定的 Environment, 就使用默认的
        if (environment == null) {
            environment = context.getStringAttribute("default");
        }
        // 处理子标签 <environment>, 同样封装成 XNode 对象
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            // id 匹配, 使用指定 id 的 Environment
            if (isSpecifiedEnvironment(id)) {
                // 事务工厂,默认使用 JDBC 管理事务
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                // 处理子标签 <dataSource>, 创建数据源工厂
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                // 根据配置创建数据源
                DataSource dataSource = dsFactory.getDataSource();
                // 这里同样使用了建造者模式
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                // 保存到 Configuration 对象中
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}
解析子标签 <dataSource>
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
        String type = context.getStringAttribute("type");
        // 将 <dataSource> 的子标签转换成 Properties 对象
        Properties props = context.getChildrenAsProperties();
        // 反射创建 DataSourceFactory 对象
        DataSourceFactory factory = (DataSourceFactory) resolveClass(type)
            .getDeclaredConstructor().newInstance();
        // 将 Properties 元素与 DataSourceFactory 绑定
        factory.setProperties(props);
        return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}

解析 <mappers> 标签

// 处理 <mappers> 标签, 可以看到, mybatis 支持四种方式导入 Mapper
//  <mapper resource="mapper/student.xml"/>
//  <mapper url="mapper/...xml"/>
//  <mapper class=""/>
//  <package name="com.gp.ibatis.dao"/>
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            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);
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // 创建用于解析 mapper。xml 配置文件的对象
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,
                    // 解析 mapper.xml 配置文件                                                     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();
                }
                // 如果使用的是 class 直接导入 Mapper 接口
                else if (resource == null && url == null && mapperClass != null) {
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    // 直接放到 configuration 对象的 mapperRegistry 集合中
                    configuration.addMapper(mapperInterface);
                }
                else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}

解析 mapper.xml 配置文件

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        // 将标签 <mapper> 封装成 XNode 对象,然后解析它的所有子标签
        configurationElement(parser.evalNode("/mapper"));
        // 标记该 mapper.xml 配置文件已加载
        configuration.addLoadedResource(resource);
        // 将 mapper.xml 文件中的 namespace 与接口绑定
        bindMapperForNamespace();
    }

    // 处理待定的对象, 即无法正常创建的对象
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

解析 <mapper> 的所有子标签

private void configurationElement(XNode context) {
    try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace == null || namespace.isEmpty()) {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
        // 标记当前正在处理的 namespace
        builderAssistant.setCurrentNamespace(namespace);
        cacheRefElement(context.evalNode("cache-ref"));
        cacheElement(context.evalNode("cache"));
        // 解析 <parameterMap> 标签, 老式的关系映射, 已被废弃
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        // 解析 <resultMap> 标签
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        sqlElement(context.evalNodes("/mapper/sql"));
        // 处理所有 <select>, <insert> ... 标签, 这应该是一个 XNode 集合
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    }
    catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
}

解析 CRUD 标签

// 最后来到 XMLMapperBuilder 的这个方法
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        // 用于解析 <select>, <insert> ... 标签的对象
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            // 解析 CRUD 标签, 封装成 MappedStatement 对象
            statementParser.parseStatementNode();
        }
        catch (IncompleteElementException e) {
            // 同样, 如果无法创建 Statement 对象, 将其放入
            // configuration 对象中的 incompleteStatement 集合中
            configuration.addIncompleteStatement(statementParser);
        }
    }
}
----------------------
// 查看 XMLStatementBuilder 的 parseStatementNode 方法
public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    // 检查使用的数据库是否统一, 否则直接返回
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        return;
    }

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    // 检查是否为 <select> 标签
    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);
    // 首先解析 <include> 标签
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // 然后解析 <selectKey> 标签
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // 最后再来解析 SQL 语句
    KeyGenerator keyGenerator;
    ....
    // 拿到 SqlSource 对象, 包含已解析的 SQL, 参数 name, JdbcType ... 
    // 解析 SQL 时, 如果是 # 用占位符替换, 如果是 $ 不处理
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ....
    // 最后封装成 MappedStatement 对象, 放入 Configuration 对象中
}

最后将 Configuration 对象返回出去, 封装到 DefaultSqlSesseionFactory 对象中

猜你喜欢

转载自blog.csdn.net/Gp_2512212842/article/details/107636221