美文网首页Mybatis
MyBatis原理解析

MyBatis原理解析

作者: 愤怒的老照 | 来源:发表于2020-06-21 11:41 被阅读0次

    流程

    • SqlSessionFactory的创建
    • SqlSession的创建
    • UserMapper代理的生成
    • 数据库查询操作执行流程

    SqlSessionFactory的创建

    SqlSessionFactory是通过加载配置文件构造

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
           // 读取配置文件流,调用parse进行转化
                XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
                var5 = this.build(parser.parse());
    }  
    

    parse.parse()是XML值进行转换,成为Configuration对象。解析主配置文件中的各个节点,然后保存在Configuration中

    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"));
                this.mapperElement(root.evalNode("mappers"));
            } catch (Exception var3) {
                throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
            }
        }
    

    最后通过Configuration对象,创建SqlSessionFactory

    public SqlSessionFactory build(Configuration config) {
            return new DefaultSqlSessionFactory(config);
        }
    

    SqlSession的创建

    SqlSession是通过SqlSessionFactory调用openSession获取

    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);
                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();
            }
    
            return var8;
        }
    

    这一步会创建四大对象之一Exector

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
            executorType = executorType == null ? this.defaultExecutorType : executorType;
            // 默认是Simple
            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
            Object executor;
            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);
            }
    
            // 设置了cacheEnabled,使用装饰器包装成CachingExecutor
            if (this.cacheEnabled) {
                executor = new CachingExecutor((Executor)executor);
            }
    
            // 插件原理
            Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
            return executor;
        }
    

    二级缓存和插件都是在这一步设置的,插件稍后整理。

    执行语句

    执行SQL语句是直接执行的下面代码

    public Object execute(SqlSession sqlSession, Object[] args) {
            Object result;
            Object param;
            switch(this.command.getType()) {
            case INSERT:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
                break;
            case UPDATE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
                break;
            case DELETE:
                param = this.method.convertArgsToSqlCommandParam(args);
                result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
                break;
            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;
            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;
            }
        }
    

    可以看到有增删改查四个分支,先看一下SELECT类型:
    首先是参数的解析,了解这一步可以明白XML的参数是如何映射的

    public Object getNamedParams(Object[] args) {
            int paramCount = this.names.size();
            if (args != null && paramCount != 0) {
                if (!this.hasParamAnnotation && paramCount == 1) {
                    // 参数没有标注@Param注解,并且参数个数为一个,就直接返回这个结果
                    return args[(Integer)this.names.firstKey()];
                } else {
                    // 否则执行这个分支,否则封装成一个Map
                    Map<String, Object> param = new ParamMap();
                    int i = 0;
    
                    for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                        Entry<Integer, String> entry = (Entry)var5.next();
                        // 一种是根据@Param的值设置键值对
                        param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);
                        //一种是param1、param2 ........
                        String genericParamName = "param" + (i + 1);
                        if (!this.names.containsValue(genericParamName)) {
                            param.put(genericParamName, args[(Integer)entry.getKey()]);
                        }
                    }
    
                    return param;
                }
            } else {
                return null;
            }
        }
    

    解析完参数后,调用selectList

        public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
            List var5;
            try {
                MappedStatement ms = this.configuration.getMappedStatement(statement);
                // 通过Executor执行query
                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;
        }
    

    进入SimpleExecutor的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 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;
        }
    

    在创建StatementHandler的同时,应用插件功能,同时创建了Mybatis四大对象中的另外两个对象:

    protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
       ……
       ……
       ……
     /**Mybatis四大对象中的ParameterHandler*/     
      this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
      /**Mybatis四大对象中的ResultSetHandler*/ 
      this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
    }
    
    
    image.png

    插件原理

    先看一下插件的接口

    public interface Interceptor {
    
      Object intercept(Invocation invocation) throws Throwable;
    
      Object plugin(Object target);
    
      void setProperties(Properties properties);
    
    }
    

    比较简单,只有3个方法。 MyBatis默认没有一个拦截器接口的实现类,开发者们可以实现符合自己需求的拦截器。

    下面的MyBatis官网的一个拦截器实例:

    @Intercepts({@Signature(
      type= Executor.class,
      method = "update",
      args = {MappedStatement.class,Object.class})})
    public class ExamplePlugin implements Interceptor {
      public Object intercept(Invocation invocation) throws Throwable {
        return invocation.proceed();
      }
      public Object plugin(Object target) {
        return Plugin.wrap(target, this);
      }
      public void setProperties(Properties properties) {
      }
    }
    

    全局xml配置:

    <plugins>
        <plugin interceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"></plugin>
    </plugins>
    

    这个拦截器拦截Executor接口的update方法(其实也就是SqlSession的新增,删除,修改操作),所有执行executor的update方法都会被该拦截器拦截到。

    下面看看是怎么实现的。
    在解析XML的时候有关于插件的解析

    this.pluginElement(root.evalNode("plugins"));
    

    解析出拦截器,添加到拦截器链,拦截器链就是一个集合的容器

    public void addInterceptor(Interceptor interceptor) {
            this.interceptorChain.addInterceptor(interceptor);
        }
    

    下面看看四大对象创建的时候,是怎么执行插件的

    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }
    
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
    }
    
    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) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }
    
    public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        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);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor, autoCommit);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }
    

    都有interceptorChain.pluginAll(statementHandler);也就是将每一个插件进行一次包装,保证最后执行的时候都可以执行到

    public Object pluginAll(Object target) {
            Inerceptor interceptor;
            for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
                interceptor = (Interceptor)var2.next();
            }
    
            return target;
        }
    

    跟进interceptor.plugin方法

    default Object plugin(Object target) {
          return Plugin.wrap(target, this);
    }
    

    这是我们之前实现插件的时候,要写的默认的一句话,有了default之后就可以不用实现了。Plugin类实现了InvocationHandler接口,很明显,我们看到这里返回了一个JDK自身提供的动态代理类。我们解剖一下这个方法调用的其他方法:

        public static Object wrap(Object target, Interceptor interceptor) {
            Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
            Class<?> type = target.getClass();
            Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
            return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
        }
    

    其中有一步是getSignatureMap,getSignatureMap方法首先会拿到拦截器这个类的 @Interceptors注解,然后拿到这个注解的属性 @Signature注解集合,然后遍历这个集合,遍历的时候拿出 @Signature注解的type属性(Class类型),然后根据这个type得到带有method属性和args属性的Method。由于 @Interceptors注解的 @Signature属性是一个属性,所以最终会返回一个以type为key,value为Set<Method>的Map。

    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
            Intercepts interceptsAnnotation = (Intercepts)interceptor.getClass().getAnnotation(Intercepts.class);
            if (interceptsAnnotation == null) {
                throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
            } else {
                Signature[] sigs = interceptsAnnotation.value();
                Map<Class<?>, Set<Method>> signatureMap = new HashMap();
                Signature[] var4 = sigs;
                int var5 = sigs.length;
    
                for(int var6 = 0; var6 < var5; ++var6) {
                    Signature sig = var4[var6];
                    Set methods = (Set)signatureMap.computeIfAbsent(sig.type(), (k) -> {
                        return new HashSet();
                    });
    
                    try {
                        Method method = sig.type().getMethod(sig.method(), sig.args());
                        methods.add(method);
                    } catch (NoSuchMethodException var10) {
                        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + var10, var10);
                    }
                }
    
                return signatureMap;
            }
        }
    

    最后调用的时候,用过invoke实现调用

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          Set<Method> methods = signatureMap.get(method.getDeclaringClass());
          if (methods != null && methods.contains(method)) {
            return interceptor.intercept(new Invocation(target, method, args));
          }
          return method.invoke(target, args);
        } catch (Exception e) {
          throw ExceptionUtil.unwrapThrowable(e);
        }
    }
    

    相关文章

      网友评论

        本文标题:MyBatis原理解析

        本文链接:https://www.haomeiwen.com/subject/tieoxktx.html