较真儿学源码系列-MyBatis核心流程源码分析

        Mybatis版本:3.5.10-SNAPSHOT。


1 简介

        MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置,并将原始类型、接口和Java POJO映射到数据库的记录中。

        现在国内的开发环境不知道从什么时候开始传出一种风气,不管开发出什么框架,都要往大而全的方向写,或者说是自己搞个生态,重复造轮子(可能是国内竞争压力大,导致越来越卷)。而MyBatis那种大道至简的源码设计风格,是非常能切中我的点的。它其实只干了一件事,就是专注于Java接口与SQL之间的映射,其他的功能也都是为了这个核心功能去服务的。所以MyBatis非常适合那些想学习源码的初学者,作为他们的第一个进行学习的源码框架。正如MyBatis源码中的pom文件中的描述所言:简单是MyBatis最大的优势。


    The MyBatis SQL mapper framework makes it easier to use a relational database with object-oriented
    applications. MyBatis couples objects with stored procedures or SQL statements using a XML descriptor or
    annotations. Simplicity is the biggest advantage of the MyBatis data mapper over object relational mapping
    tools.

1.1 MyBatis与Hibernate        

        Hibernate几乎不需要写SQL,甚至不用懂SQL的语法。只需要定义好POJO到数据库表的映射关系,即可通过Hibernate提供的方法来完成持久层操作;而MyBatis是需要自己写SQL的,工作量稍大,但是可以对SQL进行调优,更灵活。通过Mybatis的代码生成器,可以自动帮我们生成Java实体类、mapper接口和XML文件,一定程度上可以简化开发。

        在国内的环境中,MyBatis相比于Hibernate更流行些。而在国外,Hibernate用得多一些。在我看来,两者都有各自的适用场景。因为Hibernate我用得不多(只在当年毕设时有用过),所以就不妄加评论了。

1.2 MyBatis与MyBatis-Plus

        MyBatis-Plus是在MyBatis的基础上进行二次开发的框架,除了保留MyBatis的原有功能之外,还提供了类似Hibernate那样直接通过Java代码即可操作数据库的能力。但是MyBatis-Plus使用的是自己定义的语法来操作数据库,所以需要增加学习成本。它更适合那些规模小、SQL简单(最好是单表的增删改查)、需要快速进行上线的项目使用。

        我在之前的公司中有用过MyBatis-Plus,但从我个人的角度而言,我并不是特别能看好MyBatis-Plus能达到MyBatis的地位(正如其官网所言:愿景是成为MyBatis最好的搭档),甚至说能取代MyBatis(取名为“Plus”的野心),它只会满足一部分人的需求。MyBatis-Plus留给我的印象是:如果是复杂的查询语句的话,它的语法将会写得很复杂,不利于排查问题和调优。所以这也就限制住了MyBatis-Plus只适合那些简单业务的场景,而MyBatis的代码生成器又从一定程度上蚕食了这个场景下的市场。

        在这里我不想过多地去评价MyBatis-Plus这个框架,我没有达到那个高度,也不想去引战。每个人的开发经验、想法都不同,所以对于MyBatis-Plus的理解也就不同。在我看来,MyBatis-Plus是有它存在的意义的,毕竟连MyBatis官方也实现了类似的插件:mybatis-dynamic-sql。


2 配置

        以下的代码是以XML的方式为例来进行配置的,首先是构建SqlSessionFactory:

String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
//将配置文件中的配置内容加载进Configuration对象中
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

        下面就来分析一下它的build方法的实现:

/**
 * SqlSessionFactoryBuilder:
 */
public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
}

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        //解析XML
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        //走到这里,已经将配置文件中的内容解析成了Configuration对象
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            reader.close();
        } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
        }
    }
}

/**
 * XMLConfigBuilder:
 * 第11行代码处:
 */
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    //使用DOM和SAX的方式来构建XPathParser对象,将XML文件的内容进行读取并放进Configuration中
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //Configuration的构造器中会默认注册一些别名映射,例如JDBC、POOLED等等,这样就可以直接使用别名
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}

/**
 * 第13行代码处:
 */
public Configuration parse() {
    //已经解析过了就抛出异常
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    //设置解析标志位
    parsed = true;
    //解析mybatis-config.xml的节点下的内容
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

/**
 * 第56行代码处:
 */
private void parseConfiguration(XNode root) {
    try {
        // issue #117 read properties first
        //解析properties节点内容并设置进Configuration对象中
        propertiesElement(root.evalNode("properties"));
        //解析settings节点
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        //加载VFS虚拟文件系统
        loadCustomVfs(settings);
        //指定日志实现
        loadCustomLogImpl(settings);
        /*
        解析typeAliases别名节点
        TypeAliasRegistry是Configuration对象中的属性,其构造器中会默认注册一些基本类型及对象类型的别名映射,这样我们就可以直接使用这些基本类型的别名了
         */
        typeAliasesElement(root.evalNode("typeAliases"));
        /*
        添加插件
        插件最终会存入到Configuration的InterceptorChain中,通过责任链的方式调用
         */
        pluginElement(root.evalNode("plugins"));
        //加载对象工厂,用于反射实例化对象(不常用)
        objectFactoryElement(root.evalNode("objectFactory"));
        //加载对象包装工厂(不常用)
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        //加载反射工厂,用于属性和getter/setter获取(不常用)
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        //设置一些settings和默认值到Configuration对象中
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        //设置环境
        environmentsElement(root.evalNode("environments"));
        //设置数据库厂商id
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        /*
        解析typeHandler类型处理器(javaType和jdbcType之间的转换)
        跟解析别名一样,TypeHandlerRegistry也会加载一些默认的转换,也可以自定义
         */
        typeHandlerElement(root.evalNode("typeHandlers"));
        //解析mapper接口
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

/**
 * 第94行代码处:
 */
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (environment == null) {
          environment = context.getStringAttribute("default");
        }
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            if (isSpecifiedEnvironment(id)) {
                //获取transactionManager
                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对象中的Environment中
                configuration.setEnvironment(environmentBuilder.build());
                break;
            }
        }
    }
}

/**
 * 第103行代码处:
 */
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        //获取mappers节点下的内容
        for (XNode child : parent.getChildren()) {
            if ("package".equals(child.getName())) {
                /*
                <1>通过package批量的方式
                
                 */
                String mapperPackage = child.getStringAttribute("name");
                configuration.addMappers(mapperPackage);
            } else {
                /*
                <2>通过resource、classpath下读取的方式
                
                 */
                String resource = child.getStringAttribute("resource");
                /*
                <3>通过url、本地或网络资源的方式
                
                 */
                String url = child.getStringAttribute("url");
                /*
                <4>通过class、mapper接口的方式
                
                 */
                String mapperClass = child.getStringAttribute("class");
                if (resource != null && url == null && mapperClass == null) {
                    //resource解析方式
                    ErrorContext.instance().resource(resource);
                    try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        //真正解析mapper的地方
                        mapperParser.parse();
                    }
                } else if (resource == null && url != null && mapperClass == null) {
                    //url解析方式
                    ErrorContext.instance().resource(url);
                    try (InputStream inputStream = Resources.getUrlAsStream(url)) {
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        //真正解析mapper的地方
                        mapperParser.parse();
                    }
                } else if (resource == null && url == null && mapperClass != null) {
                    //class解析方式
                    Class mapperInterface = Resources.classForName(mapperClass);
                    configuration.addMapper(mapperInterface);
                } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}

/**
 * XMLMapperBuilder:
 * 第170行和第178行代码处:
 */
public void parse() {
    //如果没有解析过的话
    if (!configuration.isResourceLoaded(resource)) {
        //解析mapper
        configurationElement(parser.evalNode("/mapper"));
        //已加载的内容放进Configuration的loadedResources中
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}

/**
 * 第200行代码处:
 */
private void configurationElement(XNode context) {
    try {
        //解析namespace属性
        String namespace = context.getStringAttribute("namespace");
        if (namespace == null || namespace.isEmpty()) {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
        builderAssistant.setCurrentNamespace(namespace);
        //解析缓存引用节点
        cacheRefElement(context.evalNode("cache-ref"));
        //解析缓存节点(二级缓存)
        cacheElement(context.evalNode("cache"));
        //解析parameterMap节点(不推荐使用)
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        //解析resultMap节点
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        //解析sql节点
        sqlElement(context.evalNodes("/mapper/sql"));
        //解析增删改查节点
        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);
    }
}

/**
 * XMLMapperBuilder:
 * 第233行代码处:
 */
private void buildStatementFromContext(List list) {
    if (configuration.getDatabaseId() != null) {
        buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
}

/**
 * 第245行和第247代码处:
 */
private void buildStatementFromContext(List list, String requiredDatabaseId) {
    for (XNode context : list) {
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}

/**
 * XMLStatementBuilder:
 * 第257行代码处:
 */
public void parseStatementNode() {
    //拿到增删改查节点的id属性
    String id = context.getStringAttribute("id");
    //拿到增删改查节点的数据库厂商id
    String databaseId = context.getStringAttribute("databaseId");

    //如果和当前数据源的数据库厂商id不匹配,则直接返回
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        return;
    }

    //获取节点名称(select|insert|update|delete)
    String nodeName = context.getNode().getNodeName();
    //通过节点名称获取相应的枚举
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    //判断是否是查询语句
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //获取flushCache属性(为空的话查sql不会刷新缓存,增删改sql会)
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    //获取useCache属性(为空的话查sql会使用到缓存,增删改sql不会)
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    //是否需要分组
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    //解析include片段
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    //解析parameterType
    String parameterType = context.getStringAttribute("parameterType");
    Class parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    //获取自定义SQL脚本语言驱动
    LanguageDriver langDriver = getLanguageDriver(lang);

    // Parse selectKey after includes and remove them.
    //解析selectKey节点(自增id)
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre:  and  were parsed and removed)
    //设置自增列
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
        keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
        keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
            configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
            ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    /*
    通过XMLLanguageDriver来解析SQL脚本,具体分为:
    DynamicSqlSource:需要通过参数才能确定的SQL语句;
    RawSqlSource:不需要通过参数就能确定的SQL语句。
     */
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    //STATEMENT, PREPARED, CALLABLE,默认为PREPARED
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    //解析parameterMap属性,不写的话会通过TypeHandler类型处理器来映射
    String parameterMap = context.getStringAttribute("parameterMap");
    //解析resultType
    String resultType = context.getStringAttribute("resultType");
    Class resultTypeClass = resolveClass(resultType);
    //解析resultMap
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
        resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    //解析keyProperty,仅适用于增改语句
    String keyProperty = context.getStringAttribute("keyProperty");
    //解析keyColumn,仅适用于增改语句
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    //将解析完的select|insert|update|delete语句添加进MappedStatement对象中,并最终添加进Configuration对象中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

/**
 * XMLMapperBuilder:
 * 第203行代码处:
 */
private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
        Class boundType = null;
        try {
            boundType = Resources.classForName(namespace);
        } catch (ClassNotFoundException e) {
            // ignore, bound type is not required
        }
        if (boundType != null && !configuration.hasMapper(boundType)) {
            // Spring may not know the real resource name so we set a flag
            // to prevent loading again this resource from the mapper interface
            // look at MapperAnnotationBuilder#loadXmlResource
            configuration.addLoadedResource("namespace:" + namespace);
            //这里重点看下addMapper方法
            configuration.addMapper(boundType);
        }
    }
}

/**
 * Configuration:
 * 第376行代码处:
 */
public  void addMapper(Class type) {
    mapperRegistry.addMapper(type);
}

/**
 * MapperRegistry:
 */
public  void addMapper(Class type) {
    //判断mapper是否是接口
    if (type.isInterface()) {
        //如果已经添加进缓存了,就抛出异常
        if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
        }
        boolean loadCompleted = false;
        try {
            /*
            创建一个MapperProxyFactory,放进knownMappers缓存中
            等到后面调用sqlSession.getMapper方法时,会调用MapperProxyFactory的newInstance方法来JDK动态代理出来一个实现类(这里也就是使用到了代理模式)
             */
            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.
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            //进行解析
            parser.parse();
            loadCompleted = true;
        } finally {
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}

/**
 * MapperAnnotationBuilder:
 * 第411行代码处:
 */
public void parse() {
    String resource = type.toString();
    //如果没有解析过、放进缓存中的话
    if (!configuration.isResourceLoaded(resource)) {
        //根据mapper接口名获取XML文件并解析(之前已经解析过了,这里会直接跳过)
        loadXmlResource();
        configuration.addLoadedResource(resource);
        assistant.setCurrentNamespace(type.getName());
        parseCache();
        parseCacheRef();
        //解析注解的方式(例如@Select注解,这里不进行分析)
        for (Method method : type.getMethods()) {
            if (!canHaveStatement(method)) {
                continue;
            }
            if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
                && method.getAnnotation(ResultMap.class) == null) {
                parseResultMap(method);
            }
            try {
                parseStatement(method);
            } catch (IncompleteElementException e) {
                configuration.addIncompleteMethod(new MethodResolver(this, method));
            }
        }
    }
    parsePendingMethods();
}

/**
 * SqlSessionFactoryBuilder:
 * 第13行代码处:
 */
public SqlSessionFactory build(Configuration config) {
    //将Configuration放进DefaultSqlSessionFactory中,后面就能够拿着Configuration中的配置执行了
    return new DefaultSqlSessionFactory(config);
}

2.1 二级缓存

        在上面第225行代码处的解析二级缓存的逻辑需要特别说明一下,这里使用到了装饰者模式。MyBatis的一级缓存指的是SqlSession级别的缓存,而二级缓存指的是应用级别的缓存。一级缓存后面会讲,这里先讲一下二级缓存。二级缓存有多种实现,他们通过相互装饰,最终变成一个大的缓存集合,放进Configuration的caches属性中,如下图所示:

较真儿学源码系列-MyBatis核心流程源码分析_第1张图片

        而使用的时候是通过责任链的方式来分别调用每种缓存的能力,后面会看到。现在先来看下二级缓存的实现细节: 

/**
 * XMLMapperBuilder:
 */
private void cacheElement(XNode context) {
    if (context != null) {
        //获取缓存的一些配置
        String type = context.getStringAttribute("type", "PERPETUAL");
        Class typeClass = typeAliasRegistry.resolveAlias(type);
        String eviction = context.getStringAttribute("eviction", "LRU");
        Class evictionClass = typeAliasRegistry.resolveAlias(eviction);
        Long flushInterval = context.getLongAttribute("flushInterval");
        Integer size = context.getIntAttribute("size");
        boolean readWrite = !context.getBooleanAttribute("readOnly", false);
        boolean blocking = context.getBooleanAttribute("blocking", false);
        Properties props = context.getChildrenAsProperties();
        //重点看下这个方法
        builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
}

/**
 * MapperBuilderAssistant:
 * 第17行代码处:
 */
public Cache useNewCache(Class typeClass,
                         Class evictionClass,
                         Long flushInterval,
                         Integer size,
                         boolean readWrite,
                         boolean blocking,
                         Properties props) {
    Cache cache = new CacheBuilder(currentNamespace)
        //二级缓存默认是PerpetualCache(HashMap实现)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class))
        //这里使用到了装饰者模式,装饰了LruCache(LinkedHashMap实现,最近最少使用算法)
        .addDecorator(valueOrDefault(evictionClass, LruCache.class))
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    //最终将缓存存进Configuration对象中
    configuration.addCache(cache);
    currentCache = cache;
    return cache;
}

/**
 * CacheBuilder:
 * 第42行代码处:
 */
public Cache build() {
    //设置默认缓存实现类为PerpetualCache
    setDefaultImplementations();
    //PerpetualCache实例化
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
    if (PerpetualCache.class.equals(cache.getClass())) {
        for (Class decorator : decorators) {
          //获取上面第36行代码处的LruCache并实例化出来,这里有多个的话会一层一层包装
          cache = newCacheDecoratorInstance(decorator, cache);
          setCacheProperties(cache);
        }
        //设置其他的装饰器
        cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
        cache = new LoggingCache(cache);
    }
    return cache;
}

/**
 * 第55行代码处:
 */
private void setDefaultImplementations() {
    if (implementation == null) {
        //设置默认缓存实现类为PerpetualCache
        implementation = PerpetualCache.class;
        if (decorators.isEmpty()) {
            decorators.add(LruCache.class);
        }
    }
}

/**
 * 第63行代码处:
 */
private Cache newCacheDecoratorInstance(Class cacheClass, Cache base) {
    //拿到Cache的构造器
    Constructor cacheConstructor = getCacheDecoratorConstructor(cacheClass);
    try {
        //调用构造器进行包装
        return cacheConstructor.newInstance(base);
    } catch (Exception e) {
        throw new CacheException("Could not instantiate cache decorator (" + cacheClass + "). Cause: " + e, e);
    }
}

/**
 * 第67行代码处:
 */
private Cache setStandardDecorators(Cache cache) {
    try {
        MetaObject metaCache = SystemMetaObject.forObject(cache);
        if (size != null && metaCache.hasSetter("size")) {
            metaCache.setValue("size", size);
        }
        if (clearInterval != null) {
            //装饰者模式,将PerpetualCache装饰为ScheduledCache(一小时会清空一次缓存)
            cache = new ScheduledCache(cache);
            ((ScheduledCache) cache).setClearInterval(clearInterval);
        }
        if (readWrite) {
            //装饰者模式,将ScheduledCache装饰为SerializedCache(序列化和反序列化存储)
            cache = new SerializedCache(cache);
        }
        //装饰者模式,将SerializedCache装饰为LoggingCache(打印缓存命中的日志信息)
        cache = new LoggingCache(cache);
        //装饰者模式,将LoggingCache装饰为SynchronizedCache(方法上加上synchronized关键字,防止并发)
        cache = new SynchronizedCache(cache);
        if (blocking) {
            //装饰者模式,将SynchronizedCache装饰为BlockingCache(内部维护了一个同步锁,获取不到锁资源的时候会被阻塞,这样可以用来防止请求被穿透,避免将请求打到数据库上)
            cache = new BlockingCache(cache);
        }
        return cache;
    } catch (Exception e) {
        throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
}

        那么具体是如何进行装饰的呢?我们可以查看下LruCache的构造器实现:

public class LruCache implements Cache {

    private final Cache delegate;
    //...

    public LruCache(Cache delegate) {
        this.delegate = delegate;
        //...
    }

    //...
}

        可以看到,LruCache内部含有一个Cache类型的delegate属性,它就是需要被包装的Cache。每一个Cache内部都有这么一个delegate属性,所以通过不断调用构造器,就能将Cache链串联起来。

2.2 SqlNode

        在上面第2小节的第327行代码处,是解析SQL脚本的地方。这里也有一些设计亮点可以单独拿出来进行讲解(这里不会涉及到具体的解析过程,具体的解析使用到了OGNL表达式,感兴趣的同学可以自行查看)。这里会将SQL脚本解析成一个个的SqlNode,使用到了组合设计模式。如下图所示:

较真儿学源码系列-MyBatis核心流程源码分析_第2张图片        

        可以看到,一个SQL被解析成了一个SqlNode树,而在后面的执行SQL阶段就会调用每一个SqlNode的apply方法来进行拼接。下面来具体看下MyBatis是如何解析成一个个的SqlNode的:

/**
 * XMLLanguageDriver:
 */
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
}

/**
 * XMLScriptBuilder:
 * 第6行代码处:
 */
public XMLScriptBuilder(Configuration configuration, XNode context, Class parameterType) {
    super(configuration);
    this.context = context;
    this.parameterType = parameterType;
    //重点看下这个方法
    initNodeHandlerMap();
}

/**
 * 第19行代码处:
 * 这里会初始化一些节点处理器,后面会看到使用的地方
 */
private void initNodeHandlerMap() {
    nodeHandlerMap.put("trim", new TrimHandler());
    nodeHandlerMap.put("where", new WhereHandler());
    nodeHandlerMap.put("set", new SetHandler());
    nodeHandlerMap.put("foreach", new ForEachHandler());
    nodeHandlerMap.put("if", new IfHandler());
    nodeHandlerMap.put("choose", new ChooseHandler());
    nodeHandlerMap.put("when", new IfHandler());
    nodeHandlerMap.put("otherwise", new OtherwiseHandler());
    nodeHandlerMap.put("bind", new BindHandler());
}

/**
 * 第7行代码处:
 */
public SqlSource parseScriptNode() {
    //重点看下这个方法
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}

/**
 * 第43行代码处:
 */
protected MixedSqlNode parseDynamicTags(XNode node) {
    List contents = new ArrayList<>();
    NodeList children = node.getNode().getChildNodes();
    //获取子节点并遍历
    for (int i = 0; i < children.getLength(); i++) {
        XNode child = node.newXNode(children.item(i));
        if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
            //如果当前节点是一个文本节点的话
            String data = child.getStringBody("");
            //如果是文本节点的话,封装成TextSqlNode
            TextSqlNode textSqlNode = new TextSqlNode(data);
            //通过查看SQLNode中是否含有“${}”来判断是否是动态的SQL
            if (textSqlNode.isDynamic()) {
                //如果是动态SQL的话,最终会封装成TextSqlNode
                contents.add(textSqlNode);
                isDynamic = true;
            } else {
                //不是动态SQL,会被封装成StaticTextSqlNode
                contents.add(new StaticTextSqlNode(data));
            }
        } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
            //如果当前节点是一个元素节点的话
            String nodeName = child.getNode().getNodeName();
            //获取节点处理器,节点处理器会在上面的第26行代码处的initNodeHandlerMap方法中被初始化
            NodeHandler handler = nodeHandlerMap.get(nodeName);
            if (handler == null) {
                throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
            }
            /*
            分别调用不同节点处理器的处理方法来进行解析,最终返回对应的SqlNode
            注意:这里会使用到递归的方式来进行解析,递归调用本方法去解析子节点
             */
            handler.handleNode(child, contents);
            isDynamic = true;
        }
    }
    return new MixedSqlNode(contents);
}

3 操作

3.1 openSession方法

        通过SqlSessionFactory的openSession方法可以获取到SqlSession:

SqlSession sqlSession = sqlSessionFactory.openSession();

        SqlSession不是真正做事的类,真正做事的是Executor。所以这里也可以说是用到了门面设计模式,SqlSession提供了统一的抽象入口,也就是门面。它内部会通过调用Executor,来完成实际的逻辑操作。下面首先来看一下SqlSession的创建:

/**
 * DefaultSqlSessionFactory:
 */
@Override
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        //获取环境变量
        final Environment environment = configuration.getEnvironment();
        //获取事务工厂
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        //创建事务
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //创建SQL执行器对象
        final Executor executor = configuration.newExecutor(tx, execType);
        //最后创建并返回一个DefaultSqlSession
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

/**
 * Configuration:
 * 第19行代码处:
 */
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    //defaultExecutorType也是ExecutorType.SIMPLE,这里写了两行重复代码,猜测是代码遗留问题
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //根据执行器的类型来进行创建(SIMPLE, REUSE, BATCH)
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    //如果全局开启了缓存,则把执行器包装为CachingExecutor(二级缓存包装一级缓存,这里依然使用到了装饰者模式)
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    /*
    之前在分析配置中的XMLConfigBuilder#parseConfiguration方法中,pluginElement方法是用来添加插件的,该方法会往interceptorChain中添加一个Interceptor
    而这里就是在配置这些插件
     */
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

3.1.1 插件

        MyBatis支持插件机制,我们可以自己实现插件,像分页、读写分离等功能都可以通过插件来实现。之前我们在第2小节的pluginElement方法中已经将插件插入到interceptorChain中了,而在上面的第3.1小节的第55行代码处,会执行pluginAll方法来配置插件。下面来看下其实现:

/**
 * InterceptorChain:
 */
public Object pluginAll(Object target) {
    /*
    可以看到这里就是遍历调用每一个拦截器的plugin方法
    plugin方法的默认实现是会生成一个代理类,所以如果有多个插件的话,这里执行的效果就是代理类又被代理了,这么一直嵌套代理下去
     */
    for (Interceptor interceptor : interceptors) {
        target = interceptor.plugin(target);
    }
    return target;
}

/**
 * Interceptor:
 * 第10行代码处:
 */
default Object plugin(Object target) {
    //默认实现,自定义的插件可以覆写
    return Plugin.wrap(target, this);
}

/**
 * Plugin:
 */
public static Object wrap(Object target, Interceptor interceptor) {
    //获取@Intercepts中的@Signature中的type
    Map, Set> signatureMap = getSignatureMap(interceptor);
    Class type = target.getClass();
    Class[] interfaces = getAllInterfaces(type, signatureMap);
    //如果当前代理的类和@Intercepts中的@Signature中的type类型可以配对上
    if (interfaces.length > 0) {
        //则可以进行动态代理,返回代理类。等到具体调用executor的方法时会调用到插件的invoke方法
        return Proxy.newProxyInstance(
            type.getClassLoader(),
            interfaces,
            new Plugin(target, interceptor, signatureMap));
    }
    return target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        Set methods = signatureMap.get(method.getDeclaringClass());
        if (methods != null && methods.contains(method)) {
            /*
            如果当前方法是插件注解上的方法的话,调用插件的intercept方法
            intercept方法内部调用invocation.proceed()方法即可调用到目标方法
             */
            return interceptor.intercept(new Invocation(target, method, args));
        }
        //否则,直接调用目标方法
        return method.invoke(target, args);
    } catch (Exception e) {
        throw ExceptionUtil.unwrapThrowable(e);
    }
}

        对于上面第10行代码处的嵌套代理的解释,下面以Executor为例,进行演示。首先需要创建三个插件:

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyPlugin1 implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("执行方法前111。。。");
        Object proceed = invocation.proceed();
        System.out.println("执行方法后111。。。");
        return proceed;
    }
}

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyPlugin2 implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("执行方法前222。。。");
        Object proceed = invocation.proceed();
        System.out.println("执行方法后222。。。");
        return proceed;
    }
}

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyPlugin3 implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("执行方法前333。。。");
        Object proceed = invocation.proceed();
        System.out.println("执行方法后333。。。");
        return proceed;
    }
}

        可以看到,就是在执行目标方法的前后分别打印了日志。然后需要将插件进行配置:


    
    
    

        注意:这里的顺序是MyPlugin2->MyPlugin1->MyPlugin3,即封装的顺序。最后我们运行下程序,查看下执行结果:

执行方法前333。。。
执行方法前111。。。
执行方法前222。。。
执行方法后222。。。
执行方法后111。。。
执行方法后333。。。
[UserDO(id=1, name=name1, sex=true, phone=phone1, address=address1), UserDO(id=2, name=name2, sex=true, phone=phone2, address=address2), UserDO(id=3, name=name3, sex=true, phone=phone3, address=address3), UserDO(id=4, name=name4, sex=true, phone=phone4, address=address4), UserDO(id=5, name=name5, sex=true, phone=phone5, address=address5), UserDO(id=6, name=name6, sex=true, phone=phone6, address=address6), UserDO(id=7, name=name7, sex=true, phone=phone7, address=address7), UserDO(id=8, name=name8, sex=true, phone=phone8, address=address8), UserDO(id=9, name=name9, sex=true, phone=phone9, address=address9), UserDO(id=10, name=name10, sex=true, phone=phone10, address=address10), UserDO(id=11, name=name11, sex=true, phone=phone11, address=address11), UserDO(id=12, name=name12, sex=true, phone=phone12, address=address12), UserDO(id=13, name=name13, sex=true, phone=phone13, address=address13)]

        其实看到这个执行的顺序就能联想到栈了。下面的图展示了具体的执行原理。

较真儿学源码系列-MyBatis核心流程源码分析_第3张图片

        这里需要说明的一点是:实际目标方法的打印顺序和图中所画有些区别,这是因为我这里的目标方法是条select查询语句,而我手动打印查询结果是在程序的最后执行的,也就是在Executor执行之后打印的(SqlSession执行之后,还记得吗?SqlSession是Executor的门面),这个时候插件都已经运行完了,所以目标方法会放在最后才打印:

List list = session.selectList("org.apache.ibatis.demo.dao.UserDAO.list", UserDO.class);
System.out.println(list);

        还有一点需要说明:上面的例子是针对同一type的多个插件的执行顺序的研究。而不同type的执行顺序是在代码里写死的,后面会看到具体写死的地方:

执行方法前Executor。。。
执行方法前StatementHandler。。。
执行方法后StatementHandler。。。
执行方法前ParameterHandler。。。
执行方法后ParameterHandler。。。
执行方法前ResultSetHandler。。。
执行方法后ResultSetHandler。。。
执行方法后Executor。。。

3.2 selectOne方法

        数据操作以selectOne方法为例:

UserDO userDO = session.selectOne("org.apache.ibatis.demo.dao.UserDAO.getById", 1L);

        查看其实现:

/**
 * DefaultSqlSession:
 */
@Override
public  T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    //可以看到selectOne方法是通过selectList方法来实现的,只不过是只截取第一条数据而已
    List list = this.selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}

/**
 * 第8行代码处:
 */
@Override
public  List selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}

private  List selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
        //从Configuration中获取mappedStatements,也就是之前第2.2小节解析的SqlNode
        MappedStatement ms = configuration.getMappedStatement(statement);
        //可以看到是通过执行器来执行的sql
        return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

/**
 * 第36行代码处:
 */
private Object wrapCollection(final Object object) {
    return ParamNameResolver.wrapToMapIfCollection(object, null);
}

/**
 * ParamNameResolver:
 * 如果参数是collection/list/array的话,就包装成map形式返回(这样就可以使用“array[0]”的方式来动态传参)
 */
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
        ParamMap map = new ParamMap<>();
        map.put("collection", object);
        if (object instanceof List) {
            map.put("list", object);
        }
        Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
        return map;
    } else if (object != null && object.getClass().isArray()) {
        ParamMap map = new ParamMap<>();
        map.put("array", object);
        Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
        return map;
    }
    return object;
}

/**
 * CachingExecutor:
 * 第36行代码处:
 */
@Override
public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //解析sql
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //生成缓存key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

/**
 * MappedStatement:
 * 第80行代码处:
 */
public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
        boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
        String rmId = pm.getResultMapId();
        if (rmId != null) {
            ResultMap rm = configuration.getResultMap(rmId);
            if (rm != null) {
                hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
        }
    }

    return boundSql;
}

/**
 * DynamicSqlSource:
 * 第91行代码处:
 */
@Override
public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    //之前在第2.2小节讲了sqlNode,而这里就是通过责任链和递归的方式来执行每一个sqlNode的apply方法,最终会拼接成需要执行的sql
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    //替换动态参数,将“#{}”替换成“?”,将参数封装为parameterMappings
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
}

/**
 * CachingExecutor:
 * 第83行代码处:
 */
@Override
public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
    //是否开启二级缓存
    Cache cache = ms.getCache();
    if (cache != null) {
        //是否需要刷新缓存
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            //之前的第2.1小节说过,二级缓存使用了装饰者模式,一个包装一个。所以这里就是在一个一个调用它们的getObject方法(装饰者+责任链)
            @SuppressWarnings("unchecked")
            List list = (List) tcm.getObject(cache, key);
            if (list == null) {
                //如果二级缓存中找不到的话,就从一级缓存/数据库中查找
                list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                //加入到二级缓存中
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    //没有开启二级缓存,就直接去一级缓存/数据库中查找
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

/**
 * TransactionalCacheManager:
 * 第145行代码处:
 */
public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
}

private TransactionalCache getTransactionalCache(Cache cache) {
    /*
    这里的作用是将Cache最外面再包一层TransactionalCache
    TransactionalCache内部会暂存所有的二级缓存,其内部的putObject方法并不会直接去调用二级缓存的putObject方法,
    而是等到调用commit方法时才会去调用二级缓存的putObject方法;当调用rollback方法时会调用二级缓存的removeObject方法
     */
    return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new);
}

/**
 * BaseExecutor:
 * 第148行代码处:
 */
@SuppressWarnings("unchecked")
@Override
public  List 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());
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    //如果queryStack为0,并且需要删除一级缓存的话,就删除它
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List list;
    try {
        queryStack++;
        /*
        从一级缓存中查找
        这里的localCache会在commit或rollback方法中被清空,也就体现了一级缓存是SqlSession级别的缓存
         */
        list = resultHandler == null ? (List) localCache.getObject(key) : null;
        if (list != null) {
            //找到的话,则处理本地缓存的输出参数
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        } else {
            //一级缓存中也找不到的话,就从数据库中查找
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } finally {
        queryStack--;
    }
    if (queryStack == 0) {
        for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
        }
        // issue #601
        deferredLoads.clear();
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
        }
    }
    return list;
}

/**
 * 第204行代码处:
 */
private  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List list;
    //先占位(没搞懂?并发考虑?)
    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;
}

/**
 * SimpleExecutor:
 * 第231行代码处:
 */
@Override
public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        //拿到Connection和Statement
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}

/**
 * Configuration:
 * 第252行代码处:
 */
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //StatementHandler的插件会在此处执行
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

/**
 * RoutingStatementHandler:
 * 第266行代码处:
 */
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
        case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            //默认情况下会走这里,创建PreparedStatementHandler
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}

/**
 * PreparedStatementHandler:
 * 第284行代码处:
 */
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}

/**
 * BaseStatementHandler:
 */
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
        generateKeys(parameterObject);
        boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    //创建一个ParameterHandler(ParameterHandler的插件会在此处执行)
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    //创建一个ResultSetHandler(ResultSetHandler的插件会在此处执行)
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
    /*
    注:在上面小节的最后提到了不同type的插件的执行顺序,从这里也就能看到了。会按照Executor->StatementHandler->ParameterHandler->ResultSetHandler
    的执行顺序执行(Executor的插件会在sqlSessionFactory.openSession方法中提前执行)

    另外,通过全局搜索MyBatis的源码发现,插件的执行时机只在上面的四个对象中有介入。这也就意味着只能对上面的四个对象做插件上的增强,其它类型的插件即使写了,也不会生效
     */
}

/**
 * SimpleExecutor:
 * 第254行代码处:
 */
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //获取连接
    Connection connection = getConnection(statementLog);
    //获取statement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //处理参数
    handler.parameterize(stmt);
    return stmt;
}

/**
 * RoutingStatementHandler:
 * 第255行代码处:
 */
@Override
public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
}

/**
 * PreparedStatementHandler:
 */
@Override
public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //这里就会调用底层的JDBC去查询数据库了
    ps.execute();
    //处理结果集(内部会使用到动态代理来创建目标对象,TypeHandler来赋值对象)
    return resultSetHandler.handleResultSets(ps);
} 
  

3.3 getMapper方法

        通过SqlSession的getMapper方法可以直接获取到mapper接口,从而可以调用到相关业务方法。这种方式比直接调用SqlSession的相关方法要更好一些,避免了硬编码:

UserDAO mapper = session.getMapper(UserDAO.class);
UserDO userDO = mapper.getById(1L);

        getMapper方法的实现就很简单了,下面来看下其实现:

/**
 * DefaultSqlSession:
 */
@Override
public  T getMapper(Class type) {
    return configuration.getMapper(type, this);
}

/**
 * Configuration:
 */
public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

/**
 * MapperRegistry:
 */
@SuppressWarnings("unchecked")
public  T getMapper(Class type, SqlSession sqlSession) {
    /*
    获取缓存中的值
    还记得之前在第2小节中调用的addMapper方法吗?在其中会往knownMappers里添加MapperProxyFactory。
    而这里就是从其中拿出mapper接口
     */
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    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);
    }
}

/**
 * MapperProxyFactory:
 * 第32行代码处:
 */
public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
    //从这里就可以看到,MyBatis就是通过JDK动态代理生成的Mapper实现类
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

        getMapper方法其实只做了一件事,就是动态代理出来mapper接口的实现类,类型为MapperProxy。之后就可以直接拿着代理类调用业务方法就行了。因为代理类是MapperProxy,所以调用业务方法时会调用到其invoke方法:

/**
 * MapperProxy:
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
}

/**
 * PlainMethodInvoker:
 * 第10行代码处:
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    return mapperMethod.execute(sqlSession, args);
}

/**
 * MapperMethod:
 */
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //下面就是在根据不同的SQL语句进行不同的处理
    switch (command.getType()) {
        case INSERT: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
        }
        case UPDATE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
        }
        case DELETE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
        }
        case SELECT:
            //拿SELECT语句来举例,通过下面的逻辑可以看到,这里最终就是在调用sqlSession的相关方法进行查询
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
            } 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());
    }
    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;
}

        上面的第61行代码处可以看到(SELECT语句的单条查询),业务方法的执行底层仍然是在调用sqlSession的相关方法。


原创不易,未得准许,请勿转载,翻版必究

你可能感兴趣的:(MyBatis,MyBatis,设计模式,Spring)