MyBatisプラグインの仕組み

MyBatisでは、マッピングされたSQLステートメントの実行プロセス中に特定のポイントでメソッド呼び出しをインターセプトできます。デフォルトでは、以下のインターフェースとメソッドをインターセプトすることが可能です:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

これらのメソッドの詳細については、それぞれのメソッドシグネチャを確認するか、MyBatisのソースコードを参照してください。メソッドの動作を単に監視するだけでなく、動作を変更または上書きしたい場合は、そのメソッドの詳細な理解が必要です。MyBatisのコアモジュールを壊す可能性があるため、特に注意が必要です。

例:

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class CustomInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 実行前の処理
        Object result = invocation.proceed();
        // 実行後の処理
        return result;
    }

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

    @Override
    public void setProperties(Properties properties) {
        // プロパティ設定
    }
}

mybatis-config.xmlでプラグインを設定します:

<plugins>
    <plugin interceptor="org.example.CustomInterceptor" />
</plugins>

分析:

mybatis-config.xmlの<plugins>ノードが読み込まれる際に、プラグインはConfiguration#interceptorChainに追加されます。

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("各XMLConfigBuilderは一度しか使用できません。");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    try {
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("SQLマッパー設定の解析エラー。原因: " + e, e);
    }
}

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            String interceptorClassName = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptorClassName).newInstance();
            interceptorInstance.setProperties(properties);
            configuration.addInterceptor(interceptorInstance);
        }
    }
}

MyBatisの実行中に、Configurationによって作成される4つのオブジェクト(ParameterHandler, ResultSetHandler, StatementHandler, Executor)に対して、インターセプターチェーンが適用されます。

public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler handler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    return (ParameterHandler) interceptorChain.pluginAll(handler);
}

public ResultSetHandler createResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler handler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    return (ResultSetHandler) interceptorChain.pluginAll(handler);
}

public StatementHandler createStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler handler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    return (StatementHandler) interceptorChain.pluginAll(handler);
}

public Executor createExecutor(Transaction transaction) {
    Executor executor = new SimpleExecutor(this, transaction);
    return (Executor) interceptorChain.pluginAll(executor);
}

プラグインがインターセプト対象のインターフェースを実装している場合、プロキシオブジェクトが返されます。

public static Object wrap(Object target, Interceptor interceptor) {
    Map 0) {
        return Proxy.newProxyInstance(
                type.getClassLoader(),
                interfaces,
                new Plugin(target, interceptor, signatureMap));
    }
    return target;
}

タグ: MyBatis プラグイン インターセプト Executor ParameterHandler

5月26日 18:51 投稿