Mybatis学习(四):Mybatis源码深度分析

   日期:2020-08-06     浏览:97    评论:0    
核心提示:Mybatis学习(四):Mybatis源码深度分析前言一、Mybatis的架构设计二、Mybatis的源码分析三、Mybatis框架之设计模式前言接上篇:Mybatis学习(三):Mybatis注解开发、缓存使用和插件使用的深度分析本篇笔者将深入学习下Mybatis的架构设计、源码分析、Mybatis框架所用到的Java设计模式一、Mybatis的架构设计二、Mybatis的源码分析三、Mybatis框架之设计模式...

Mybatis学习(四):Mybatis源码深度分析

  • 前言
  • 一、源码环境准备
  • 二、Mybatis的架构设计
    • 2.1 框架基础支撑层
      • 2.1.1 reflection包——反射模块
      • 2.1.2 type包——类型模块
      • 2.1.3 logging包——日志模块
      • 2.1.4 io包——IO模块
      • 2.1.5 parsing包——解析器模块
      • 2.1.6 datasource包——数据源模块
      • 2.1.7 transaction包——事务模块
      • 2.1.8 cache包——缓存模块
      • 2.1.9 binding包——Binding 模块
      • 2.1.10 annotations包——注解模块
      • 2.1.11 exceptions包——异常模块
    • 2.2 数据处理核心层
      • 2.2.1 配置解析
        • 2.2.1.1 builder包——配置解析过程
        • 2.2.1.2 mapping包——SQL 操作解析后的映射
      • 2.2.2 SQL 解析
      • 2.2.3 SQL 执行
      • 2.2.4 插件
    • 2.3 API接口层
  • 三、Mybatis的源码分析
    • 3.1 Mybatis运行总体流程
    • 3.2 传统XML配置方式源码分析
      • 3.2.1 读取配置文件
        • 3.2.1.1 < mappers />标签的解析
        • 3.2.1.2 其他标签的解析
      • 3.2.2 执行SQL流程的源码分析
    • 3.3 mapper代理方式源码分析
      • 3.3.1 读取配置文件
      • 3.3.2 执行SQL
  • 四、Mybatis框架之设计模式
  • 五、详细源码分析

前言

接上篇:Mybatis学习(三):Mybatis注解开发、缓存使用和插件使用的深度分析

本篇笔者将深入学习下Mybatis的架构设计、源码分析、Mybatis框架所用到的Java设计模式

一、源码环境准备

Mybatis官方仓库地址: https://github.com/mybatis/mybatis-3

本人使用的是3.5.3版本。

二、Mybatis的架构设计


如上图所示,MyBatis 的整体架构分为三层:

2.1 框架基础支撑层

包含整个 MyBatis 的基础模块,为数据处理核心层提供支持。

2.1.1 reflection包——反射模块

对 Java 原生的反射进一步封装,提供了更简洁易用的 API来支持上层调用,对反射操作进行了优化。

2.1.2 type包——类型模块

  1. 提供MyBatis 的别名机制;
  2. 提供SQL语句绑定参数和映射结果集时 JDBC 类型与 Java 类型之间的转换方法;

2.1.3 logging包——日志模块

  1. 提供日志输出信息
  2. 集成多种第三方日志框架(Log4j、 Log4j2、Slf4j、jdk14)

2.1.4 io包——IO模块

资源加载模块,主要是对类加载器进行封装,确定类加载器的使用顺序,并提供了加载类文件以及其他资源文件的功能 。

2.1.5 parsing包——解析器模块

  1. 提供基于Java XPath 解析器XPathParser,为 MyBatis 初始化时解析 mybatis-config.xml 配置文件以及映射配置文件提供支持;
  2. 处理动态 SQL 语句中的占位符;

2.1.6 datasource包——数据源模块

  1. 提供JNDI、池化、非池化的数据源实现
  2. 提供了与第三方数据源集成的接口

2.1.7 transaction包——事务模块

  1. 提供基于容器管理的事务实现类和基于 JDBC 的事务实现类;
  2. 一般会与 Spring 框架集成,并由 Spring 框架管理事务

2.1.8 cache包——缓存模块

  1. 提供了一级缓存和二级缓存。
  2. 提供Cache缓存容器接口。

2.1.9 binding包——Binding 模块

在调用 SqlSession 相应方法执行数据库操作时,需要指定映射文件中定义的 SQL 节点,如果出现拼写错误,我们只能在运行时才能发现相应的异常。为了尽早发现这种错误,MyBatis 通过 Binding 模块,将用户自定义的 Mapper 接口与映射配置文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。

开发人员无须编写自定义 Mapper 接口的实现,MyBatis 会自动为其创建动态代理对象。

2.1.10 annotations包——注解模块

MyBatis 提供了注解的方式。

2.1.11 exceptions包——异常模块

定义了 MyBatis 专有的 PersistenceException 和 TooManyResultsException 异常。

2.2 数据处理核心层

实现MyBatis 的初始化以及完成一次数据库操作的涉及的全部流程 。

2.2.1 配置解析

2.2.1.1 builder包——配置解析过程

  1. 提供MyBatis 的XML配置和注解实现类;
  2. 提供SQL构造器和结果集解析器

2.2.1.2 mapping包——SQL 操作解析后的映射

  1. 提供参数映射;
  2. 提供结果集映射;
  3. SQL语句的映射;
  4. SQL语句的封装

2.2.2 SQL 解析

对应 scripting 包。

实现动态 SQL 语句,提供多种动态 SQL语句对应标签处理器;

2.2.3 SQL 执行

对应 executor 和 ==cursor ==包。前者对应执行器,后者对应执行结果的游标。

2.2.4 插件

对应 plugin包

MyBatis 提供了插件接口,我们可以通过添加用户自定义插件的方式对 MyBatis 进行扩展。

2.3 API接口层

对应 session包
核心是 SqlSession 接口。

三、Mybatis的源码分析

3.1 Mybatis运行总体流程

  1. 加载配置文件并初始化(一种是xml配置文件,包括主配置文件config.xml和mapper.xml文件;一种是Java代码中以注解的形式注入的配置),将配置信息解析封装到Configuration类中,将SQL的配置信息封装到mappedstatement类对象中,放入内存;
  2. 调用Mybatis提供的API时,传入SQL的id和参数对象,将请求传入下层的数据处理层;
  3. 接到API调用请求后,根据传入的SQL的ID查找MappedStatement对象,根据传入的参数对象解析MappedStatement对象,得到最终要执行的SQL语句和入参,获取数据库连接,执行SQL语句并得到执行结果,根据MappedStatement对象中结果映射配置进行结果集数据转换,释放资源;
  4. 将上一步转换后的结果返回。

3.2 传统XML配置方式源码分析

3.2.1 读取配置文件

首先我们需要加载所有Mybatis的配置文件

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

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

此处利用配置文件转成的输入流new SqlSessionFactoryBuilder().build(resourceAsStream)构建了SqlSessionFactory类,接下来我们一步步的深入分析,配置文件是如何解析的。

    // 1.我们最初调用的build
    public SqlSessionFactory build(InputStream inputStream) {
        //调用了重载方法
        return build(inputStream, null, null);
    }

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

    public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return build(inputStream, null, properties);
    }

    // 2.调用的重载方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类
            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.
            }
        }
    }

方法中创建了一个XMLConfigBuilder类,这个类是专门解析mybatis的配置文件的类。

    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }

    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        // 创建 Configuration 对象
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        // 设置 Configuration 的 variables 属性
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }
  1. XMLConfigBuilder类先创建了一个XPathParser类
        public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
            commonConstructor(validation, variables, entityResolver);
            this.document = createDocument(new InputSource(inputStream));
        }
    	    
    	    private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    	        this.validation = validation;
    	        this.entityResolver = entityResolver;
    	        this.variables = variables;
    	        // 创建 XPathFactory 对象
    	        XPathFactory factory = XPathFactory.newInstance();
    	        this.xpath = factory.newXPath();
    	    }
    	    
    	    private Document createDocument(InputSource inputSource) {
    	        // important: this must only be called AFTER common constructor
    	        try {
    	            // 1> 创建 DocumentBuilderFactory 对象
    	            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    	            factory.setValidating(validation); // 设置是否验证 XML
    	
    	            factory.setNamespaceAware(false);
    	            factory.setIgnoringComments(true);
    	            factory.setIgnoringElementContentWhitespace(false);
    	            factory.setCoalescing(false);
    	            factory.setExpandEntityReferences(true);
    	
    	            // 2> 创建 DocumentBuilder 对象
    	            DocumentBuilder builder = factory.newDocumentBuilder();
    	            builder.setEntityResolver(entityResolver); // 设置实体解析器
    	            builder.setErrorHandler(new ErrorHandler() { // 实现都空的
    	
    	                @Override
    	                public void error(SAXParseException exception) throws SAXException {
    	                    throw exception;
    	                }
    	
    	                @Override
    	                public void fatalError(SAXParseException exception) throws SAXException {
    	                    throw exception;
    	                }
    	
    	                @Override
    	                public void warning(SAXParseException exception) throws SAXException {
    	                }
    	
    	            });
    	            // 3> 解析 XML 文件
    	            return builder.parse(inputSource);
    	        } catch (Exception e) {
    	            throw new BuilderException("Error creating document instance. Cause: " + e, e);
    	        }
    	    }
    
    创建 DocumentBuilder 对象解析XML文件。
  2. 创建 Configuration 对象,并设置 Configuration对象的 variables 属性。
  3. 调用XMLConfigBuilder类的parse()方法
        
        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;
        }
    
  4. 解析 XML configuration 节点(parseConfiguration(parser.evalNode("/configuration"));
        
        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);
            }
        }
    
    该方法里的每个子方法是解析config.xml里每个标签,并将解析结果封装到Configuration对象中。
    我们重点看一下 < mappers /> 标签的解析方法mapperElement(root.evalNode("mappers"));

3.2.1.1 < mappers />标签的解析

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        // 遍历子节点
        for (XNode child : parent.getChildren()) {
            // 如果是 package 标签,则扫描该包
            if ("package".equals(child.getName())) {
                // 获得包名
                String mapperPackage = child.getStringAttribute("name");
                // 添加到 configuration 中
                configuration.addMappers(mapperPackage);
            // 如果是 mapper 标签,
            } else {
                // 获得 resource、url、class 属性
                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);
                    // 获得 resource 的 InputStream 对象
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // 创建 XMLMapperBuilder 对象
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                    // 执行解析
                    mapperParser.parse();
                // 使用完全限定资源定位符(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();
                // 使用映射器接口实现类的完全限定类名
                } 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.");
                }
            }
        }
    }
}
  • 首先如果是 package 标签,则扫描该包,添加到 configuration 中。

     public void addMappers(String packageName) {
         // 扫描该包下所有的 Mapper 接口,并添加到 mapperRegistry 中
         mapperRegistry.addMappers(packageName);
     }
    

    执行MapperRegistry 对象的addMappers方法

        
    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 void addMappers(String packageName) {
        addMappers(packageName, Object.class);
    }
    
        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 中
                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);
                }
            }
        }
    }
    

    调用MapperAnnotationBuilder类的parse()方法解析注解;

        
    public void parse() {
        // 判断当前 Mapper 接口是否应加载过。
        String resource = type.toString();
        if (!configuration.isResourceLoaded(resource)) {
            // 加载对应的 XML Mapper
            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();
    }
    

    调用parsePendingMethods();方法,这个方法里面又调用MapperBuilderAssistant类的addMappedStatement方法构建MappedStatement对象并添加到 configuration 中。这样就完成了mapper配置信息的解析封装。

  • 如果是 mapper 标签,不管是使用相对于类路径的资源引用还是使用完全限定资源定位符(URL),都是先获取对应的文件输入流、创建 XMLMapperBuilder 对象、执行解析

    // 获得 resource 的 InputStream 对象
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 创建 XMLMapperBuilder 对象
    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
    // 执行解析
    mapperParser.parse();
    

    调用XMLMapperBuilder.parse();方法

    public void parse() {
        // 判断当前 Mapper 是否已经加载过
        if (!configuration.isResourceLoaded(resource)) {
            // 解析 `<mapper />` 节点
            configurationElement(parser.evalNode("/mapper"));
            // 标记该 Mapper 已经加载过
            configuration.addLoadedResource(resource);
            // 绑定 Mapper
            bindMapperForNamespace();
        }
    
        // 解析待定的 <resultMap /> 节点
        parsePendingResultMaps();
        // 解析待定的 <cache-ref /> 节点
        parsePendingCacheRefs();
        // 解析待定的 SQL 语句的节点
        parsePendingStatements();
    }
    

    解析 <mapper /> 节点的configurationElement(parser.evalNode("/mapper"));方法中,有一行buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

    // 解析 <select /> <insert /> <update /> <delete /> 节点们
    private void buildStatementFromContext(List<XNode> list) {
        if (configuration.getDatabaseId() != null) {
            buildStatementFromContext(list, configuration.getDatabaseId());
        }
        buildStatementFromContext(list, null);
        // 上面两块代码,可以简写成 buildStatementFromContext(list, configuration.getDatabaseId());
    }
    
    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        //遍历 <select /> <insert /> <update /> <delete /> 节点们
        for (XNode context : list) {
            // 创建 XMLStatementBuilder 对象,执行解析
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                // 解析失败,添加到 configuration 中
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }
    

    创建XMLStatementBuilder对象,执行解析statementParser.parseStatementNode();

    
    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);
    }
    

    最终还是调用了MapperBuilderAssistant类的addMappedStatement方法创建MappedStatement 对象并存入configuration 中。

  • 如果是使用映射器接口实现类的完全限定类名,先获得 Mapper 接口,再调用MapperRegistry 对象的addMapper方法,基本同第一种情况。

3.2.1.2 其他标签的解析

关于其他标签的解析过程,笔者自行阅读,此处暂时先不做介绍了。

3.2.2 执行SQL流程的源码分析

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

    // 4.(1)根据statementid来从Configuration中map集合中获取到了指定的MappedStatement对象
       //(2)将查询任务委派了executor执行器
    List<Object> objects = sqlSession.selectList("namespace.id");

一般我们先创建出一个SqlSession对象(sqlSessionFactory.openSession()

//6. 进入openSession方法
@Override
public SqlSession openSession() {
    //getDefaultExecutorType()传递的是SimpleExecutor
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

调用openSessionFromDataSource方法

//7. 进入openSessionFromDataSource。
//ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务
//openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        // 获得 Environment 对象
        final Environment environment = configuration.getEnvironment();
        // 创建 Transaction 对象
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 创建 Executor 对象
        final Executor executor = configuration.newExecutor(tx, execType);
        // 创建 DefaultSqlSession 对象
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        // 如果发生异常,则关闭 Transaction 对象
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

最终创建了DefaultSqlSession对象。接下来我们以查询方法为例子,看看sql语句具体是如何执行的。

//8.进入selectList方法,多个重载方法
@Override
public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
}

@Override
public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // 获得 MappedStatement 对象
        MappedStatement ms = configuration.getMappedStatement(statement);
        // 执行查询
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

到这里我们看到最终调用了Executor执行器的query方法。此方法在SimpleExecutor的父类BaseExecutor中实现

@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示
    BoundSql boundSql = ms.getBoundSql(parameter);
    //为本次查询创建缓存的Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    // 查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    // 已经关闭,则抛出 ExecutorException 异常
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List<E> list;
    try {
        // queryStack + 1
        queryStack++;
        // 从一级缓存中,获取查询结果
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        // 获取到,则进行处理
        if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        // 获得不到,则从数据库中查询
        } else {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } finally {
        // queryStack - 1
        queryStack--;
    }
    if (queryStack == 0) {
        // 执行延迟加载
        for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
        }
        // issue #601
        // 清空 deferredLoads
        deferredLoads.clear();
        // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
        }
    }
    return list;
}

先从一级缓存中获取查询结果,如果获得不到,则从数据库中查询。

// 从数据库中读取操作
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    // 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 `DeferredLoad#canLoad()` 方法
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        // 执行读操作
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        // 从缓存中,移除占位对象
        localCache.removeObject(key);
    }
    // 添加到缓存中
    localCache.putObject(key, list);
    // 暂时忽略,存储过程相关
    if (ms.getStatementType() == StatementType.CALLABLE) {
        localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

调用SimpleExecutordoQuery方法。

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        // 传入参数创建StatementHanlder对象来执行查询
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 创建jdbc中的statement对象
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行 StatementHandler ,进行读操作
        return handler.query(stmt, resultHandler);
    } finally {
        // 关闭 StatementHandler 对象
        closeStatement(stmt);
    }
}

我们看到在这个方法中最终创建了StatementHandler对象,由StatementHandler对象执行具体的SQL。

 // 初始化 StatementHandler 对象
 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
     Statement stmt;
     // 获得 Connection 对象
     Connection connection = getConnection(statementLog);
     // 创建 Statement 或 PrepareStatement 对象
     stmt = handler.prepare(connection, transaction.getTimeout());
     // 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符
     handler.parameterize(stmt);
     return stmt;
 }

首选获得 Connection 对象,创建出Statement对象(PrepareStatement),执行parameterize(Statement statement)方法完成对SQL语句占位符的赋值。(具体的看下边ParameterHandler的默认实现类DefaultParameterHandler类里的setParameters方法)

@Override
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 遍历 ParameterMapping 数组
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            // 获得 ParameterMapping 对象
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                // 获得值
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                // 获得 typeHandler、jdbcType 属性
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                // 设置 ? 占位符的参数
                try {
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException | SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

执行StatementHandler.query(Statement statement, ResultHandler resultHandler)方法

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行查询
    ps.execute();
    // 处理返回结果
    return resultSetHandler.handleResultSets(ps);
}

执行SQL后,调用ResultSetHandlerhandleResultSets方法将结果集转换为出参的类型。

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List<Object> 对象。
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。
    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 获得 ResultMap 数组
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount); // 校验
    while (rsw != null && resultMapCount > resultSetCount) {
        // 获得 ResultMap 对象
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 处理 ResultSet ,将结果添加到 multipleResults 中
        handleResultSet(rsw, resultMap, multipleResults, null);
        // 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象
        rsw = getNextResultSet(stmt);
        // 清理
        cleanUpAfterHandlingResultSet();
        // resultSetCount ++
        resultSetCount++;
    }

    // 因为 `mappedStatement.resultSets` 只在存储过程中使用,本系列暂时不考虑,忽略即可
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
        while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
    }

    // 如果是 multipleResults 单元素,则取首元素返回
    return collapseSingleResultList(multipleResults);
}

至此SQL的执行就完了。

3.3 mapper代理方式源码分析

3.3.1 读取配置文件

同3.2.1中一样。

3.3.2 执行SQL

// 使用JDK动态代理对mapper接口产生代理对象
IUserMapper mapper = sqlSession.getMapper(IUserMapper.class);

//代理对象调用接口中的任意方法,执行的都是动态代理中的invoke方法
List<Object> allUser = mapper.findAllUser();

在解析配置文件的时候我们发现,在Configuration对象中有个MapperRegistry的属性,这个类中维护了一个Hash集合,存放的就是mapper接口对象的代理工厂类。


//这个类中维护一个HashMap存放MapperProxyFactory
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

sqlSession.getMapper(IUserMapper.class)的实现是DefaultSqlSession类的getMapper方法。

@Override
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
}

调用configuration对象的getMapper(Class<T> type, SqlSession sqlSession)方法,

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

进一步调用mapperRegistry对象的getMapper(Class<T> type, SqlSession sqlSession)方法;

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 获得 MapperProxyFactory 对象
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    // 不存在,则抛出 BindingException 异常
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    /// 通过动态代理工厂生成实例。
    try {
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

调用了mapperProxyFactorynewInstance(SqlSession sqlSession)方法

//MapperProxyFactory类中的newInstance方法
public T newInstance(SqlSession sqlSession) {
    // 创建了JDK动态代理的invocationHandler接口的实现类mapperProxy
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    // 调用了重载方法
    return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {

    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

上边的方法中MapperProxy类实现了InvocationHandler接口, 所以mapper接口虽然我们没写具体的实现方法,但是Mybatis会使用动态代理(JDK动态代理)的方式为我们创建一个代理实现类。

调用mapper的查询方法,实际上是代理对象调用方法。即MapperProxy<T>类的invoke(Object proxy, Method method, Object[] args)方法。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 如果是 Object 定义的方法,直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);

        } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    // 获得 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 重点在这:MapperMethod最终调用了执行的方法
    return mapperMethod.execute(sqlSession, args);
}

最终MapperMethod执行了方法

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判断mapper中的方法类型,最终调用的还是SqlSession中的方法
    switch (command.getType()) {
        case INSERT: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 执行 INSERT 操作
            // 转换 rowCount
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
        }
        case UPDATe: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 转换 rowCount
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
        }
        case DELETE: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 转换 rowCount
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
        }
        case SELECT:
            // 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
            // 执行查询,返回列表
            } else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
            // 执行查询,返回 Map
            } else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
            // 执行查询,返回 Cursor
            } else if (method.returnsCursor()) {
                result = executeForCursor(sqlSession, args);
            // 执行查询,返回单个对象
            } else {
                // 转换参数
                Object param = method.convertArgsToSqlCommandParam(args);
                // 查询单条
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional() &&
                        (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + command.getName());
    }
    // 返回结果为 null ,并且返回类型为基本类型,则抛出 BindingException 异常
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
                + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    // 返回结果
    return result;
}

接下来的sqlSession.insert等方法的调用和传统XML配置里的调用一样。

四、Mybatis框架之设计模式

Mybatis框架使用了大量的设计模式

设计模式 Mybatis框架的应用
Builder模式 SqlSessionFactoryBuilder等
工厂模式 SqlSessionFactory、TransactionFactory、LogFactory等
单例模式 ErrorContext等
代理模式 MapperProxy等
组合模式 SqlNode和各个子类ChooseSqlNode等
模板方法模式 BaseExecutor和SimpleExecutor
适配器模式 对jdbc、log4j 等日志的实现
装饰者模式 Cache包下cache.decorators 等子包中各个装饰者的实现
迭代器模式 PropertyTokenizer类

五、详细源码分析

本篇暂时追踪了下Mybatis的加载配置和SQL执行过程,不够详细和彻底。

下一篇我们对全框架做个整体的源码分析。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服