Mybatis源码分析

1. 解析配置文件,创建SQLSessionFactory

InputStream inputStream = CommonTest.class.getClassLoader().getResourceAsStream("mybatis-configuration.xml");
SQLSessionFactory SQLSessionFactory =
                new SQLSessionFactoryBuilder().build(inputStream);

这一步首先读取了mybatis的configuration xml配置文件,用这个流构造了Factory的Builder,它底层是使用Mybatis自己的XMLConfigBuilder解析器去解析了这个Configuration文件, 然后调用了解析器的parse方法,SQLSessionFactory就被构造出来了:

   public SQLSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SQLSessionFactory var5;
        try {

            //文件解析器
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

            //构造SQLSessionFactory
            var5 = this.build(parser.parse());

        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SQLSession.", var14);
        } finally {
            ErrorContext.instance().reset();

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

        }

        return var5;
    }

根据上面代码可知,SQLSessionFactory被创建的核心是 XMLConfigBuilder的 parse方法, 也就是解析文件的那个步骤,它又是怎么解析的呢?

初次学Mybatis的时候,配置的那个Configuration全局文件里有很多属性对吧,各种节点, environment,mapper,setting...的,可想它内部肯定是对这些节点做了解析的:

    private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));

            //解析全局配置文件的各个节点,并加载配置
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));

            //解析mapper的各个元素(SQL,resultmap......)    
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

从上面代码可以分析,其实解析是大概分为2个部分的,一个是解析全局配置文件里的属性, 一个是解析mapper文件的各个属性,对已经学过mybatis的同学来说, 都知道mybatis底层是使用了动态代理模式来操作接口方法的,那么从第二个部分:解析mapper的部分就尤为重要了.

先看第一个部分,其实其第一个部分最重要的一点就是分析出了,mybatis所有的属性, 配置全部由一个Configuration对象保存了起来,随便抽一个方法:

    private void settingsElement(Properties props) {
        this.configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
        this.configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
        this.configuration.setCacheEnabled(this.booleanValueOf(props.getProperty("cacheEnabled"), true));
        this.configuration.setProxyFactory((ProxyFactory)this.createInstance(props.getProperty("proxyFactory")));
        this.configuration.setLazyLoadingEnabled(this.booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
        this.configuration.setAggressiveLazyLoading(this.booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
        this.configuration.setMultipleResultSetsEnabled(this.booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
        this.configuration.setUseColumnLabel(this.booleanValueOf(props.getProperty("useColumnLabel"), true));
        this.configuration.setUseGeneratedKeys(this.booleanValueOf(props.getProperty("useGeneratedKeys"), false));
        ......

    }

再看看Configuration类的属性,就印证了之前说过的, Configuration就是一个贯穿的mybatis整个生命周期的核心配置类:

public class Configuration {
    protected Environment environment;
    protected boolean safeRowBoundsEnabled;
    protected boolean safeResultHandlerEnabled;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel;
    protected boolean cacheEnabled;
    protected boolean callSettersOnNulls;
    protected boolean useActualParamName;
    protected boolean returnInstanceForEmptyRow;
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected LocalCacheScope localCacheScope;
    protected JdbcType jdbcTypeForNull;
    protected Set<String> lazyLoadTriggerMethods;
   ......
   ......
  }

之前说过,XMLConfigBuilder负责解析mybatis全局配置文件,而解析阶段又大致分为2个阶段:解析基本属性; 解析mapper文件.解析的基本属性都存放在了全局的Configuration之中. 再看mapperElement方法,也就是开始解析的那个方法,可以直接锁定XMLMapperBuilder类, 看这个类的名字就知道它是解析mapper文件的:

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

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);

                            //开始解析mapper文件
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSQLFragments());
                            mapperParser.parse();

                           } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSQLFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

在说解析mapper文件之前,先想想mapper文件里有什么,最核心的就那几个:resultMap,statement(也就是SQL), cache缓存,那么XMLMapperBuilder肯定也是围绕那个几个去解析的,或者还解析了其他的东西:

    public void parse() {
        if (!this.configuration.isResourceLoaded(this.resource)) {

            //这里是解析Mapper文件的核心,它内部把Mapper的select,delete,update,insert这些
            //标签添加到了MapperStatement内,也就是SQL语句
            this.configurationElement(this.parser.evalNode("/mapper"));

            this.configuration.addLoadedResource(this.resource);

            //这一步也是非常重要的,它绑定了mapper文件的命名空间
            this.bindMapperForNamespace();
        }

        //解析还没有解析的各个元素
        this.parsePendingResultMaps();
        this.parsePendingCacheRefs();
        this.parsePendingStatements();
    }

其实mybatis解析mapper文件的步骤还是比较深入的,这里由于篇幅关系,怕说长了,就脱离此文的目的,还是以理解为主,直到mybatis做了哪些事情就行,但还是需要去看看mybatis到底是如何解析mapp文件的,也就是 上面的:

this.configurationElement(this.parser.evalNode("/mapper"));
......

最后,回到解析Configuration文件的起点,解析完文件后,SQLSessionFactory就被build方法构造出来了,其实他是个DefaultSQLSessionFactory:

   public SQLSessionFactory build(Configuration config) {
        return new DefaultSQLSessionFactory(config);
    }

总结下SQLSessionFactory被创建的过程: 首先Mybatis使用XMLConfigBuilder文件解析器,解析全局配置文件,XMLMapperBuilder,解析mapper文件,解析完后将所有的属性封装在了Configuration对象中,然后使用这个全局的Configuration对象构造了DefaultSQLSessionFactory.

2. 开启java程序和数据库之间的会话:

   SQLSession SQLSession = SQLSessionFactory.openSession();

从第一步创建SQLSessionFactory的过程可知,SQLSessionFactory是一个DefaultSQLSessionFactory,所以openSession也是调用了DefaultSQLSessionFactory的openSession:

   public SQLSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }

可以看到openSession是调用了openSessionFromDataSource方法,那它是怎么实现的呢:

    private SQLSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSQLSession var8;
        try {
            Environment environment = this.configuration.getEnvironment();

            //事物管理
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

            //核心Executor执行器
            Executor executor = this.configuration.newExecutor(tx, execType);

            //SQLSession就是DefaultSQLSession
            var8 = new DefaultSQLSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }

从上面代码可以看出,SQLSession是DefaultSQLSession,然后还创建了一个Executor这个核心的执行器对象,那么首先看看这个Executor是什么,为什么说它是核心呢? 首先看看它内部的方法吧:

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;

    int update(MappedStatement var1, Object var2) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSQL var6) throws SQLException;

    <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;

    <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;

    List<BatchResult> flushStatements() throws SQLException;

    void commit(boolean var1) throws SQLException;

    void rollback(boolean var1) throws SQLException;
    ...
    ...
}

从Executor内部方法可以看出,它负责执行Statement的执行操作.既然它是一个接口,那么必然有对应的实现类,这里先看configuration的newExecutor方法是怎么创建它的吧:

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object executor;

        //批处理的Executor
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        }
        //可复用的Executor
        else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } 
        //简单的Executor,如果不做配置,那么就默认是它了
        else {
            executor = new SimpleExecutor(this, transaction);
        }

        //缓存Executor,这里就是二级缓存的关键之处,把已经创建好的Executor,装成CachingExecutor
        if (this.cacheEnabled) {
            executor = new CachingExecutor((Executor)executor);
        }
        //插件拓展,原来插件是在创建Executor的时候被封装的.
        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

从上面代码就可以分析出:创建SQLSession的时机其实是创建Executor的时机,也是封装plugin的时机, 也可以猜测Executor就是Mybatis的核心组件之一,负责执行一系列的SQL(Statement).

总结下第二步获取SQLSession的过程: 使用DefaultSQLSessionFactory的Configuration创建出对应类型的Executor, 并封装配置中的插件,再使用Executor和Configuration创建DefaultSQLSession, 由此可见Configuration从被构建出来,流转到了DefaultSQLSession之中.

3. 获取mapper代理对象:

        PersonMapper personMapper = SQLSession.getMapper(PersonMapper.class);

已经知到了上面返回的PersonMapper是一个MapperProxy对象,那么它是怎么被创建出来的呢? 回想下上面的几个步骤,DefaultSQLSession包含了Configuration,而Configuration是解析的全局配置文件和mapper文件被构造出来的,Configuration也包含了相应的属性, 所以MapperProxy应该也是从Configuration获取:

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

-----------------------------------------------------------
   public <T> T getMapper(Class<T> type, SQLSession SQLSession) {
        return this.mapperRegistry.getMapper(type, SQLSession);
    }

可以看到最终是由MapperRegistry对象获取的,那MapperRegistry是如何获取的呢:

    ...
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public <T> T getMapper(Class<T> type, SQLSession SQLSession) {
        //获取mapper接口对应的工厂
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                //使用工厂创建mapper接口
                return mapperProxyFactory.newInstance(SQLSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

MapperRegistry内部有一个map,保存着mapper接口的class到相应的MapperProxyFactory的工厂,当我们需要获取某个mapper接口的时候,就利用相对的工厂创建mapper接口代理对象.

需要搞清楚的是MapperProxyFactory是如何创建Mapper接口代理对象的呢?直接锁定newInstance方法:

    protected T newInstance(MapperProxy<T> mapperProxy) {
        //jdk动态代理
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SQLSession SQLSession) {
        //首先创建MapperProxy对象,MapperProxy对象实现了InvocationalHandler接口,所以它可以被jdk动态代理
        MapperProxy<T> mapperProxy = new MapperProxy(SQLSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }

上面代码就不解释了,直接总结第3步吧: Configuration从MapperRegistry里获取对应的Mapper接口的代理工厂MapperProxyFactory,MapperProxyFactory使用jdk动态代理创建Mapper接口的动态代理对象.

4. 执行mapper接口方法:

  personMapper.selectPersonById(1L);

上面分析到由SQLSession获取的Mapper对象其实是MapperProxyFactory创建的MapperProxy代理对象,那么SQL代码的执行也肯定是在MapperProxy类的invoke中了,所以直接锁定MapperProxy类的invoke方法:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            //如果是Object类的方法
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

             //jdk8后的接口允许默认方法,所以在这里做判断
            if (method.isDefault()) {
                if (privateLookupInMethod == null) {
                    return this.invokeDefaultMethodJava8(proxy, method, args);
                }

                return this.invokeDefaultMethodJava9(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        MapperMethod mapperMethod = this.cachedMapperMethod(method);
         //真正执行Statement的入口
        return mapperMethod.execute(this.SQLSession, args);
    }

根据invoke方法可以分析出,invoke方法执行的也就是MapperMethod的execute方法,那MapperMethod是什么呢?先猜下:在mybatis所有核心组件基本都是xxxHandler命名,所有与接口有关的基本都是mapperxx命名,mapperProxy是接口代理对象,mapperMethod就有可能是接口的执行方法. 看下mapperProxy的属性:

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private static final int ALLOWED_MODES = 15;
    private static final Constructor<Lookup> lookupConstructor;
    private static final Method privateLookupInMethod;
    private final SQLSession SQLSession;
    private final Class<T> mapperInterface;
    //存储着mapperProxy对应接口的方法
    private final Map<Method, MapperMethod> methodCache;
        ....
    }

再看刚才在invoke方法里的一个细节:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       .....
        //根据接口要调用的方法,获取对应的mapperMethod
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        //使用获取到的mapperMethod执行SQL
        return mapperMethod.execute(this.SQLSession, args);
    }

已经可以确定了,在调用mapper接口方法的时候,mapperProxy内部已经维护了对应接口的所有方法,只等我们调用的时候execute执行了.

那mapperMethod是如何执行的呢?

    public Object execute(SQLSession SQLSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {

         //insert操作
        case INSERT:
            param = this.method.convertArgsToSQLCommandParam(args);
            result = this.rowCountResult(SQLSession.insert(this.command.getName(), param));
            break;

        //update操作
        case UPDATE:
            param = this.method.convertArgsToSQLCommandParam(args);
            result = this.rowCountResult(SQLSession.update(this.command.getName(), param));
            break;

        //delete操作
        case DELETE:
            param = this.method.convertArgsToSQLCommandParam(args);
            result = this.rowCountResult(SQLSession.delete(this.command.getName(), param));
            break;

        //select操作
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(SQLSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(SQLSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(SQLSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(SQLSession, args);
            } else {
                param = this.method.convertArgsToSQLCommandParam(args);
                result = SQLSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;

        //flush操作
        case FLUSH:
            result = SQLSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

我的selectPersonById方法是SELECT,就看看SELECT执行的逻辑吧:

  case SELECT:
            //如果方法没有返回值
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(SQLSession, args);
                result = null;
            }
            //如果方法返回集合
             else if (this.method.returnsMany()) {
                result = this.executeForMany(SQLSession, args);
            } 
            //如果方法返回map
            else if (this.method.returnsMap()) {
                result = this.executeForMap(SQLSession, args);
            }
            //返回游标类
            else if (this.method.returnsCursor()) {
                result = this.executeForCursor(SQLSession, args);
            } else {
                //正常普通返回值

                //抓换
                param = this.method.convertArgsToSQLCommandParam(args);
                result = SQLSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }

锁定最后一个 selectOne 方法:

    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
            //获取mapperStatement
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            //执行器执行
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }

        return var5;
    }

可以看到SQLSession的select还是代理到Executor的query方上了:

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //获取SQL
        BoundSQL boundSQL = ms.getBoundSQL(parameter);
        //创建二级缓存的key,这个key非常长
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSQL);
        //查询
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSQL);
    }

继续深入重载的query方法:

    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());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            //上一个缓存已经查询完成,并且标注了FlushCache=true的属性,那么清空本地缓存
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
                //如果没有指定resultHandler,那么线程本地缓存查询
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                    //如果有缓存,就覆盖当前参数值,但只针对CALLABLE
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSQL);
                } else {
                    //从数据库查询
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSQL);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator var8 = this.deferredLoads.iterator();

                while(var8.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                //查询完清除缓存
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }

继续深入 queryFromDatabase 方法,看Executor到底是怎么查询的:

    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSQL boundSQL) throws SQLException {
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            //又是一个接口方法,有需要深入
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSQL);
        } finally {
            this.localCache.removeObject(key);
        }

        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }

继续看doQuery方法,因为我没有指定Executor的类型,所以这个doQuery肯定是在SimpleExecutor中了:

    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSQL boundSQL) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
            //来了,又是一个核心对象
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSQL);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

在doQuery方法中根据Configuration创建了StatementHandler,它是SQL的处理器,看看Configuration是怎么创建它的:

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSQL boundSQL) {
        //RoutingStatementHandler
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSQL);
        //再次对插件进行了封装
        StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

上面最重要的创建RoutingStatementHandler那句代码,RoutingStatementHandler是个什么东西,看样子它是路由的StatementHandler,岂不是创建各种类型的StatementHandler:

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSQL boundSQL) {
        switch(ms.getStatementType()) {
        //普通的StatementHandler
        case STATEMENT:
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSQL);
            break;
        //预编译StatementHandler,也就是预编译的SQL
        case PREPARED:
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSQL);
            break;
        //CALLABLE类型的StatementHandler
        case CALLABLE:
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSQL);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }

从RoutingStatementHandler的源码可知,它负责创建不同类型的SQL的StatementHandler.

那么创建完这个StatementHandler后,有啥用呢? 回到doQuery方法:

    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSQL boundSQL) throws SQLException {
        Statement stmt = null;

        List var9;
        try {
            Configuration configuration = ms.getConfiguration();
           //创建StatementHandler
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSQL);
             //预编译StatemenT    
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            var9 = handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return var9;
    }

可以看到下面的一个 prepareStatement 方法直接预编译出来了一个Statement,Statement相信各位同学不陌生吧,java原生的SQL操作啊,由StatementHandler预编译成Statement这个方法肯定是做些事情的,到现在还没有设置参数呢,而且参数一直都随着那几颗方法:

   private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Connection connection = this.getConnection(statementLog);
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
         //设置参数  
        handler.parameterize(stmt);
        return stmt;
    }

看看parameterize是如何设置参数的吧:

  public void parameterize(Statement statement) throws SQLException {
        //ParameterHandler出来了,它时StatementHandler的一个属性,负责SQL的入参
        this.parameterHandler.setParameters((PreparedStatement)statement);
    }

继续看看ParameterHandler是如何完成入参的吧:

   public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());

        //从BoundSQL中获取ParameterMapping,也就是参数
        List<ParameterMapping> parameterMappings = this.boundSQL.getParameterMappings();
        if (parameterMappings != null) {
            for(int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                //如果参数是输入参数
                if (parameterMapping.getMode() != ParameterMode.OUT) {

                    //这里就是获取参数的对应的属性名
                    String propertyName = parameterMapping.getProperty();

                    //参数值
                    Object value;
                    //获取参数值
                    if (this.boundSQL.hasAdditionalParameter(propertyName)) {
                        value = this.boundSQL.getAdditionalParameter(propertyName);
                    } else if (this.parameterObject == null) {
                        value = null;

                    //如果 TypeHandler已经有了这个参数值的类型
                    } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                        value = this.parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
                        value = metaObject.getValue(propertyName);
                    }

                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
                        //TypeHandler设置参数,内部仍然是Statement设置参数
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (SQLException | TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    }
                }
            }
        }

    }

上面设置完参数后,回到doQuery方法,使用StatementHandler的query方法执行:

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

最后看看ResultSetHandler是如何处理结果集的吧:

  public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId());
        List<Object> multipleResults = new ArrayList();
        int resultSetCount = 0;

        //获取第一个结果集
        ResultSetWrapper rsw = this.getFirstResultSet(stmt);
        List<ResultMap> resultMaps = this.mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        this.validateResultMapsCount(rsw, resultMapCount);
        //一个resultMap对应一个结果集,不断遍历
        while(rsw != null && resultMapCount > resultSetCount) {
            ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
            //处理结果集
            this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
            //获取下一个结果集
            rsw = this.getNextResultSet(stmt);
            this.cleanUpAfterHandlingResultSet();
            ++resultSetCount;
        }

        String[] resultSets = this.mappedStatement.getResultSets();
        if (resultSets != null) {
            while(rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);
                    this.handleResultSet(rsw, resultMap, (List)null, parentMapping);
                }

                rsw = this.getNextResultSet(stmt);
                this.cleanUpAfterHandlingResultSet();
                ++resultSetCount;
            }
        }

        return this.collapseSingleResultList(multipleResults);
    }

算是完成了对mybatis执行过程的一个简单的源码分析吧,由于我功力浅薄, 即使是分析出来了这么一个大致的运行流程,其中的大部分细节我仍然是不懂的,所以我会继续学习.

mybatis源码总结

Mybatis最核心的对象莫过于Configuration了, Configuration在解析完配置文件和mapper文件后就一直流转于整个mybatis执行的生命周期内。

首先由Configuration创建出Executor,从而创建DefaultSQLSession, 又由Configuration内的MapperRegistry获取MapperProxy对象,执行SQL的时候, 也由Configuration创建StatementHandler,几乎可以说Configuration是无处不在。


然后说下Mybatis核心的组件:Executor。

负责调度StatementHandler。 StatementHandler负责调度ParameterHandler对Statement 进行参数处理,执行Statement调用ResultSetHandler对SQL执行的结果做出封装。

ParameterHandler负责Statement的参数处理, ResultSetHandler负责Statement执行后的结果集处理.

在分析mybatis源码的过程中,我觉得mybatis整个框架的设计和面向对象的思想是发挥的淋漓尽致的。

其实有很多人说mybatis不够智能化,但是我想说的是, Mybatis帮我们做掉这么多繁琐的事情,还能让我们灵活的掌握SQL,在设计上实属np。 听说Hibernate不需要写SQL,我没学过,不好妄下定论。 但是我觉得SQL本身就不属于Java语言这个范畴,如果连SQL都不需要写,是什么ORM框架,又怎么谈SQL优化呢?

results matching ""

    No results matching ""