从 Mybatis Plugin 看 Mybatis 核心组件功

2022-06-21  本文已影响0人  逗逗罗

Mybatis 是一个常用 ORM 持久层框架。其在动态 SQL 执行的基础功能上,提供了插件的功能。Mybatis 插件通过切面编程的方式,无侵入的进行功能扩展。最常见的插件有分页插件(PageHelper)。

Mybatis Plugin 源码分析

mybatis 插件的核心功能代码主要在 "org.apache.ibatis.plugin" 包下,主要包括以下类。

Mybatis Plugin 调用链

Plugin 类通过 wrap 方法生成对应的代理类, wrap 方法的调用链如下:

Configuration
    -> newParameterHandler
        -> interceptorChain.pluginAll
            -> interceptor.plugin
                -> Plugin.wrap(target, this)
    -> newStatementHandler
        -> interceptorChain.pluginAll
            -> interceptor.plugin
                -> Plugin.wrap(target, this)
    -> newExecutor
        -> interceptorChain.pluginAll
            -> interceptor.plugin
                -> Plugin.wrap(target, this)
    -> newResultSetHandler
        -> interceptorChain.pluginAll
            -> interceptor.plugin
                -> Plugin.wrap(target, this)

从整条调用链可以看出 Mybatis 的插件可以增强的功能类有四个:
1. ParameterHandler
2. StatementHandler
3. Executor
4. ResultSetHandler
而这四个类正是 Mybatis 的四大组件,四个类共同完成 JDBC 的相关操作。

Mybatis 四大组件

  1. ParameterHandler
    负责将用户传递的参数转换为 JDBC Statement 需要的参数

  2. StatementHandler
    封装 JDBC Statement 相关的操作,比如参数的设置、将 Statement 的结果集转换为 List

  3. ResultSetHandler
    负责将 JDBC 返回的 ResultSet 结果集转换为 List 类型集合

  4. Executor
    MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护

Mybatis 的核心架构图如下:


Mybatis.png

Mybatis Plugin 应用实例

了解了 Mybatis Plugin 源码及相关组件功能后, 通过 “执行SQL打印” 的扩展功能再熟悉 Mybatis Plugin 的使用。 完成一个 Mybatis Plugin 的核心是构建一个类实现Interceptor 接口,对于 Spring 应用来说将该实现类注入至IOC容器即可。

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

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }
        String sqlId = mappedStatement.getId();
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        Configuration configuration = mappedStatement.getConfiguration();
        long start = System.currentTimeMillis();
        Object returnValue = invocation.proceed();
        long time = System.currentTimeMillis() - start;
        printSql(configuration, boundSql, time, sqlId);
        return returnValue;
    }

    private static void printSql(Configuration configuration, BoundSql boundSql, long time, String sqlId) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        //替换空格、换行、tab缩进等
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", getParameterValue(obj));
                    }
                }
            }
        }
        logs(time, sql, sqlId);
    }

    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(obj) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value.replace("$", "\\$");
    }

    private static void logs(long time, String sql, String sqlId) {
        StringBuilder sb = new StringBuilder()
                .append(" Time:").append(time)
                .append(" ms - ID:").append(sqlId)
                .append(StringPool.NEWLINE).append("Execute SQL:")
                .append(sql).append(StringPool.NEWLINE);
        log.info(sb.toString());
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties0) {
    }
}
上一篇 下一篇

猜你喜欢

热点阅读