C/C++教程

mybatis 源码分析之创建sqlSessionFactory

本文主要是介绍mybatis 源码分析之创建sqlSessionFactory,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

mybatis 源码分析之sqlSessionFactory的创建过程

  • sqlSessionFactory是什么?
  • 一、通过SqlSessionFactoryBuilder的build方法创建SqlSessionFactory源码详解
    • buid方法--使用构建者模式
    • parser.parse()源码--生成Configuration 对象
    • parseConfiguration()源码
    • 解析properties标签
    • 解析 settings 标签
    • 将setting标签中的name和value解析到properties中
    • 解析 typeAliases 标签,将配置类注册到TypeAliasRegistry中。给类取的别名
    • 解析 mappers 标签--也就是我们写的具体表的xml文件
    • 根据配置的mapper所在的包进行解析addMappers
    • 解析待定的 resultMap 节点
    • 解析待定的 cache-ref 节点
    • 解析待定的 SQL语句的节点
  • 总结

sqlSessionFactory是什么?

构建者模式:使用多个简单的对象一步一步构建 成一个复杂的对象
代码使用例子

    // 1. 读取配置文件,读成字节输入流,注意:现在还没解析
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    // 2. 解析配置文件,封装Configuration对象   创建DefaultSqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    // 3. 生产了DefaultSqlsession实例对象   设置了事务不自动提交  完成了executor对象的创建
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 4.(1)根据statementid来从Configuration中map集合中获取到了指定的MappedStatement对象
       //(2)将查询任务委派了executor执行器
    User user =  sqlSession.selectOne("com.share.mapper.IUserMapper.findById",1);
    System.out.println(user);
    // 5.释放资源
    sqlSession.close();

一、通过SqlSessionFactoryBuilder的build方法创建SqlSessionFactory源码详解

buid方法–使用构建者模式

构建者模式:使用多个简单的对象一步一步构建 成一个复杂的对象

  // 1.我们最初调用的build
    public SqlSessionFactory build(InputStream inputStream) {
        //调用了重载方法
        return build(inputStream, null, null);
    }
     // 2.调用的重载方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类
            // 里面会生成XPathParser 解析对象 创建 Document 对象 对XML进行验证
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 执行 XML 解析
            // 创建 DefaultSqlSessionFactory 对象
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }

parser.parse()源码–生成Configuration 对象

/**
     * 解析 XML 成 Configuration 对象。
     *
     * @return Configuration 对象
     */
    public Configuration parse() {
        // 若已解析,抛出 BuilderException 异常
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        // 标记已解析
        parsed = true;
        ///parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签
        // 解析 XML configuration 节点
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }

parseConfiguration()源码

 /**
     * 解析 XML
     *
     * 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
     *
     * @param root 根节点
     */
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 解析 <properties /> 标签
            propertiesElement(root.evalNode("properties"));
            // 解析 <settings /> 标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            // 加载自定义的 VFS 实现类
            loadCustomVfs(settings);
            // 解析 <typeAliases /> 标签
            typeAliasesElement(root.evalNode("typeAliases"));
            // 解析 <plugins /> 标签
            pluginElement(root.evalNode("plugins"));
            // 解析 <objectFactory /> 标签
            objectFactoryElement(root.evalNode("objectFactory"));
            // 解析 <objectWrapperFactory /> 标签
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 解析 <reflectorFactory /> 标签
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // 赋值 <settings /> 到 Configuration 属性
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 解析 <environments /> 标签
            environmentsElement(root.evalNode("environments"));
            // 解析 <databaseIdProvider /> 标签
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 解析 <typeHandlers /> 标签
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析 <mappers /> 标签
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

解析properties标签

 /**
     * 1. 解析 <properties /> 标签,成 Properties 对象。
     * 2. 覆盖 configuration 中的 Properties 对象到上面的结果。
     * 3. 设置结果到 parser 和 configuration 中
     *
     * @param context 节点
     * @throws Exception 解析发生异常
     */
    private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
            // 读取子标签们,为 Properties 对象
            Properties defaults = context.getChildrenAsProperties();
            // 读取 resource 和 url 属性
            String resource = context.getStringAttribute("resource");
            String url = context.getStringAttribute("url");
            if (resource != null && url != null) { // resource 和 url 都存在的情况下,抛出 BuilderException 异常
                throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
            }
            // 读取本地 Properties 配置文件到 defaults 中。
            if (resource != null) {
                defaults.putAll(Resources.getResourceAsProperties(resource));
                // 读取远程 Properties 配置文件到 defaults 中。
            } else if (url != null) {
                defaults.putAll(Resources.getUrlAsProperties(url));
            }
            // 覆盖 configuration 中的 Properties 对象到 defaults 中。
            Properties vars = configuration.getVariables();
            if (vars != null) {
                defaults.putAll(vars);
            }
            // 设置 defaults 到 parser 和 configuration 中。
            parser.setVariables(defaults);
            configuration.setVariables(defaults);
        }
    }

解析 settings 标签

 /**
     * 将 <setting /> 标签解析为 Properties 对象
     *
     * @param context 节点
     * @return Properties 对象
     */
    private Properties settingsAsProperties(XNode context) {
        // 将子标签,解析成 Properties 对象
        if (context == null) {
            return new Properties();
        }
        Properties props = context.getChildrenAsProperties();
        // Check that all settings are known to the configuration class
        // 校验每个属性,在 Configuration 中,有相应的 setting 方法,否则抛出 BuilderException 异常
        MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
        for (Object key : props.keySet()) {
            if (!metaConfig.hasSetter(String.valueOf(key))) {
                throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
            }
        }
        return props;
    }

将setting标签中的name和value解析到properties中

 public Properties getChildrenAsProperties() {
        Properties properties = new Properties();
        for (XNode child : getChildren()) {
            String name = child.getStringAttribute("name");
            String value = child.getStringAttribute("value");
            if (name != null && value != null) {
                properties.setProperty(name, value);
            }
        }
        return properties;
    }

解析 typeAliases 标签,将配置类注册到TypeAliasRegistry中。给类取的别名

   /**
     * 解析 <typeAliases /> 标签,将配置类注册到 {@link org.apache.ibatis.type.TypeAliasRegistry} 中。
     *
     * @param parent 节点
     */
    private void typeAliasesElement(XNode parent) {
        if (parent != null) {
            // 遍历子节点
            for (XNode child : parent.getChildren()) {
                // 指定为包的情况下,注册包下的每个类
                if ("package".equals(child.getName())) {
                    String typeAliasPackage = child.getStringAttribute("name");
                    configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
                // 指定为类的情况下,直接注册类和别名
                } else {
                    String alias = child.getStringAttribute("alias");
                    String type = child.getStringAttribute("type");
                    try {
                        Class<?> clazz = Resources.classForName(type); // 获得类是否存在
                        // 注册到 typeAliasRegistry 中
                        if (alias == null) {
                            typeAliasRegistry.registerAlias(clazz);
                        } else {
                            typeAliasRegistry.registerAlias(alias, clazz);
                        }
                    } catch (ClassNotFoundException e) { // 若类不存在,则抛出 BuilderException 异常
                        throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
                    }
                }
            }
        }
    }

解析 mappers 标签–也就是我们写的具体表的xml文件

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            // 遍历子节点
            for (XNode child : parent.getChildren()) {
                // 如果是 package 标签,则扫描该包
                if ("package".equals(child.getName())) {
                    // 获取 <package> 节点中的 name 属性
                    String mapperPackage = child.getStringAttribute("name");
                    // 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置
                    configuration.addMappers(mapperPackage);
                // 如果是 mapper 标签,
                } else {
                    // 获得 resource、url、class 属性
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");

                    // resource 不为空,且其他两者为空,则从指定路径中加载配置
                    if (resource != null && url == null && mapperClass == null) {
                        ErrorContext.instance().resource(resource);
                        // 获得 resource 的 InputStream 对象
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        // 创建 XMLMapperBuilder 对象
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        // 执行解析
                        mapperParser.parse();
                        // url 不为空,且其他两者为空,则通过 url 加载配置
                    } else if (resource == null && url != null && mapperClass == null) {
                        ErrorContext.instance().resource(url);
                        // 获得 url 的 InputStream 对象
                        InputStream inputStream = Resources.getUrlAsStream(url);
                        // 创建 XMLMapperBuilder 对象
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        // 执行解析
                        mapperParser.parse();
                        // mapperClass 不为空,且其他两者为空,则通过 mapperClass 解析映射配置
                    } else if (resource == null && url == null && mapperClass != null) {
                        // 获得 Mapper 接口
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        // 添加到 configuration 中
                        configuration.addMapper(mapperInterface);
                        // 以上条件不满足,则抛出异常
                    } else {
                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                }
            }
        }
    }

根据配置的mapper所在的包进行解析addMappers

根据配置的dao层中的类解析对应的namespace的xml中的方法与类中方法的关联用于后面的调用

public void addMappers(String packageName) {
        // 扫描该包下所有的 Mapper 接口,并添加到 mapperRegistry 中
        mapperRegistry.addMappers(packageName);
    }
    /**
     * 扫描指定包,并将符合的类,添加到 {@link #knownMappers} 中
     *
     * @since 3.2.2
     */
    public void addMappers(String packageName, Class<?> superType) {
        // 扫描指定包下的指定类
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        // 遍历,添加到 knownMappers 中
        for (Class<?> mapperClass : mapperSet) {
            addMapper(mapperClass);
        }
    }
public <T> void addMapper(Class<T> type) {
        // 判断,必须是接口。
        if (type.isInterface()) {
            // 已经添加过,则抛出 BindingException 异常
            if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // 添加到 knownMappers 中   
                 //这个类中维护一个HashMap存放MapperProxyFactory 
                 //private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
                knownMappers.put(type, new MapperProxyFactory<>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                // 解析 Mapper 的注解配置
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                parser.parse();
                // 标记加载完成
                loadCompleted = true;
            } finally {
                // 若加载未完成,从 knownMappers 中移除
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }
 /**
     * 解析注解
     */
    public void parse() {
        // 判断当前 Mapper 接口是否应加载过。
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            // 加载对应的 XML Mapper 解析里面的select标签等等并封装到MappedStatement类中
            loadXmlResource();
            // 标记该 Mapper 接口已经加载过
            configuration.addLoadedResource(resource);
            // 设置 namespace 属性
            assistant.setCurrentNamespace(type.getName());
            // 解析 @CacheNamespace 注解
            parseCache();
            // 解析 @CacheNamespaceRef 注解
            parseCacheRef();
            // 遍历每个方法,解析其上的注解
            Method[] methods = type.getMethods();
            for (Method method : methods) {
                try {
                    // issue #237
                    if (!method.isBridge()) {
                        // 执行解析
                        parseStatement(method);
                    }
                } catch (IncompleteElementException e) {
                    // 解析失败,添加到 configuration 中
                    configuration.addIncompleteMethod(new MethodResolver(this, method));
                }
            }
        }
        // 解析待定的方法
        parsePendingMethods();
    }
 /**
     * 加载对应的 XML Mapper
     */
    private void loadXmlResource() {
        // Spring may not know the real resource name so we check a flag
        // to prevent loading again a resource twice
        // this flag is set at XMLMapperBuilder#bindMapperForNamespace
        // 判断 Mapper XML 是否已经加载过,如果加载过,就不加载了。
        // 此处,是为了避免和 XMLMapperBuilder#parse() 方法冲突,重复解析
        if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
            // 获得 InputStream 对象
            String xmlResource = type.getName().replace('.', '/') + ".xml";
            // #1347
            InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
            if (inputStream == null) {
                // Search XML mapper that is not in the module but in the classpath.
                try {
                    inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
                } catch (IOException e2) {
                    // ignore, resource is not required
                }
            }
            // 创建 XMLMapperBuilder 对象,执行解析
            if (inputStream != null) {
                XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
                xmlParser.parse();
            }
        }
    }

解析待定的 resultMap 节点

// 创建 ResultMap 对象,并添加到 Configuration 中。
    public ResultMap addResultMap(
            String id,
            Class<?> type,
            String extend,
            Discriminator discriminator,
            List<ResultMapping> resultMappings,
            Boolean autoMapping) {
        // 获得 ResultMap 编号,即格式为 `${namespace}.${id}` 。
        id = applyCurrentNamespace(id, false);
        // 获取完整的 extend 属性,即格式为 `${namespace}.${extend}` 。从这里的逻辑来看,貌似只能自己 namespace 下的 ResultMap 。
        extend = applyCurrentNamespace(extend, true);

        // 如果有父类,则将父类的 ResultMap 集合,添加到 resultMappings 中。
        if (extend != null) {
            // 获得 extend 对应的 ResultMap 对象。如果不存在,则抛出 IncompleteElementException 异常
            if (!configuration.hasResultMap(extend)) {
                throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
            }
            ResultMap resultMap = configuration.getResultMap(extend);
            // 获取 extend 的 ResultMap 对象的 ResultMapping 集合,并移除 resultMappings
            List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
            extendedResultMappings.removeAll(resultMappings);
            // Remove parent constructor if this resultMap declares a constructor.
            // 判断当前的 resultMappings 是否有构造方法,如果有,则从 extendedResultMappings 移除所有的构造类型的 ResultMapping 们
            boolean declaresConstructor = false;
            for (ResultMapping resultMapping : resultMappings) {
                if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
                    declaresConstructor = true;
                    break;
                }
            }
            if (declaresConstructor) {
                extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
            }
            // 将 extendedResultMappings 添加到 resultMappings 中
            resultMappings.addAll(extendedResultMappings);
        }
        // 创建 ResultMap 对象
        ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
                .discriminator(discriminator)
                .build();
        // 添加到 configuration 中
        configuration.addResultMap(resultMap);
        return resultMap;
    }

解析待定的 cache-ref 节点

 /**
     * 获得指向的 Cache 对象
     *
     * @param namespace 指向的命名空间
     * @return Cache 对象
     */
    public Cache useCacheRef(String namespace) {
        if (namespace == null) {
            throw new BuilderException("cache-ref element requires a namespace attribute.");
        }
        try {
            unresolvedCacheRef = true; // 标记未解决
            // 获得 Cache 对象
            Cache cache = configuration.getCache(namespace);
            // 获得不到,抛出 IncompleteElementException 异常
            if (cache == null) {
                throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
            }
            // 记录当前 Cache 对象
            currentCache = cache;
            unresolvedCacheRef = false; // 标记已解决
            return cache;
        } catch (IllegalArgumentException e) {
            throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
        }
    }

解析待定的 SQL语句的节点

 /**
     * 执行解析
     */
    public void parseStatementNode() {
        // 获得 id 属性,编号。
        String id = context.getStringAttribute("id");
        // 获得 databaseId , 判断 databaseId 是否匹配
        String databaseId = context.getStringAttribute("databaseId");
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
            return;
        }

        // 获得各种属性
        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
        String resultMap = context.getStringAttribute("resultMap");
        String resultType = context.getStringAttribute("resultType");
        String lang = context.getStringAttribute("lang");

        // 获得 lang 对应的 LanguageDriver 对象
        LanguageDriver langDriver = getLanguageDriver(lang);

        // 获得 resultType 对应的类
        Class<?> resultTypeClass = resolveClass(resultType);
        // 获得 resultSet 对应的枚举值
        String resultSetType = context.getStringAttribute("resultSetType");
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
        // 获得 statementType 对应的枚举值
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

        // 获得 SQL 对应的 SqlCommandType 枚举值
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        // 获得各种属性
        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 对象,并替换 <include /> 标签相关的内容
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());

        // Parse selectKey after includes and remove them.
        // 解析 <selectKey /> 标签
        processSelectKeyNodes(id, parameterTypeClass, langDriver);

        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        // 创建 SqlSource 对象
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        // 获得 KeyGenerator 对象
        String resultSets = context.getStringAttribute("resultSets");
        String keyProperty = context.getStringAttribute("keyProperty");
        String keyColumn = context.getStringAttribute("keyColumn");
        KeyGenerator keyGenerator;
        // 优先,从 configuration 中获得 KeyGenerator 对象。如果存在,意味着是 <selectKey /> 标签配置的
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = configuration.getKeyGenerator(keyStatementId);
        // 其次,根据标签属性的情况,判断是否使用对应的 Jdbc3KeyGenerator 或者 NoKeyGenerator 对象
        } else {
            keyGenerator = context.getBooleanAttribute("useGeneratedKeys", // 优先,基于 useGeneratedKeys 属性判断
                    configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) // 其次,基于全局的 useGeneratedKeys 配置 + 是否为插入语句类型
                    ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }

        // 创建 MappedStatement 对象
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
                fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
                resultSetTypeEnum, flushCache, useCache, resultOrdered,
                keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }

总结

描述了创建SqlSessionFactory时大致的过程,主要是加载配置文件读取成流并封装到configuration中,将xml中的sql语句封装到MappedStatement中,大量使用构建模式和动态代理模式等来创建对象。

这篇关于mybatis 源码分析之创建sqlSessionFactory的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!