MyBatis(5)-MyBatisのプラグインメカニズムを深く理解する
21237 ワード
MyBatis(5)-MyBatisのプラグインメカニズムを深く理解する
MyBatisは、プラグインメカニズムをカスタマイズするための拡張を提供し、本質的にはブロッカーの機能を実現しています.
個人ホームページ:tuzhenyu’s page原文アドレス:MyBatis(五)—MyBatisのプラグインメカニズムを深く理解する
(0)カードメカニズム MyBatisでは、プラグインを使用して特定のメソッド呼び出しをブロックできます.特定のブロックポイントには、次の4種類があります. Executer:update()、query()、flushStatement()、commit()、rollback()、getTransaction()、close()、isClosed()などの方法; ParameterHandler:getParameterObject()、setParameters()などの方法; ResultSetHandler:handleResultSets(),handleOutputParameters(); StatementHandler:prepare(),parameterize(),batch(),update(),query()
コンフィギュレーションオブジェクトを初期化すると、解析ラベルがInterceptorインスタンスを生成し、ブロッキングチェーンInterceptorChainを追加します.プラグイン生成のタイミングはExecutor,ParameterHandler,ResultSet Handler,StatementHandlerの4つのインタフェースの実装クラス初期化の際にpluginAll()メソッドが呼び出され,InterceptorChainブロッキングチェーンのブロッキングに基づいてエージェントクラスを生成するか否かが決定される. ブロッキングが実行される順序は、Executor、ParameterHandler、ResultSet Handler、StatamentHandlerである.
(1)カードの使用プラグインクラスExamplePlugin実装インタフェースInterceptorを作成し、主なプラグインロジックはinterceptor()メソッドで実装される. はconfig.xmlプロファイルにplugins構成を追加します.ノードは順序付きです. properties/settings/typeAliases/typeHandlers/objectFactory/objectWrapperFactory/plugins/environments/databaseIdProvider/mappersなのでplugins構成はenvironmentとmappersの前に必要です.
(2)プラグインメカニズムの実現プロセス
1.プラグイン構成の初期化コンフィギュレーションオブジェクトが初期化されると、pluginElement()メソッドを呼び出してラベルを解析します. タグ対応intercptorクラス名を取得し、反射により対応するブロッカークラスインスタンスinterceptorを生成する. ラベルの下のすべてのラベルを取得し、Propertiesオブジェクトに解析し、Propertiesをブロッカーに設定します. 生成されたブロッキングインスタンスinterceptorをconfigurationに入れる.
addInterceptor()メソッドを呼び出し、configurationの属性InterceptorChainにInterceptorインスタンスを入れる.InterceptorChainはInterceptorのチェーンテーブルです.
2.ブロッキングの実行フロー
Executorのブロッカーは、sqlSession生成フェーズでExecutorインスタンスを作成するときにpluginAll()メソッドでブロッカーを追加します.
ParameterHandler,ResultSet Handler,StatamentHandlerブロッキングはSQLの特定の実行フェーズでpluginAll()メソッドでブロッキングを追加します. StatementHandler初期化時にpluginAll()メソッドを呼び出してブロッキング を追加する. StatementHandler初期化時にParameterHandlerとResultSetHandlerインスタンス が作成されます. ParameterHandler初期化時にpluginAll()メソッドを呼び出してブロッキング を追加 ResultSetHandler初期化時にpluginAll()メソッドを呼び出してブロッキングを追加
ブロッキングの追加プラグイン生成のタイミングはpluginAll()メソッドを呼び出すときであり、ここで形参targetはExecutor、ParameterHandler、ResultSet Handler、StatamentHandlerインタフェースの実装クラスであり、ブロックチェーンInterceptorChainのすべてのブロックを巡り、一致すればtargetのエージェントクラスを作成する.ブロックチェーン内の複数のブロックで生成されるエージェントクラスはネスト構造であり、すなわち、前のブロックで生成されたエージェントクラスに対してエージェント生成エージェントクラスのエージェントクラスを再実行する. は、ブロッカー内のplugin()メソッドを呼び出してマッチングし、マッチングすると対応するエージェントクラスを生成する. plugin()メソッドは主にExecutor,ParameterHandler,ResultSet Handler,StatamentHandlerの実装クラス生成エージェントであり,これらのクラスのメソッドを呼び出すときにInvocationHandlerのinvoke()メソッドを直接呼び出す. pluginメソッドでMyBatisが提供する既存の生成エージェントを呼び出すメソッドPlugin.wrap()メソッド は、ブロックの要求に適合するすべての方法署名 を取得する.
getSignatureMap()メソッド取得メソッド署名を呼び出し、すべてのSignatureラベルコンテンツを巡り、type(Executor.class)の下でmethodを満たすすべてのメソッドを取得し、メソッド署名マッピングを構築する. メソッド署名マッピングは、Map である. SignatureMapメソッドラベルマッピングを取得した後、type(Executor.class)を介してtargetのすべてのインタフェースを取得し、メソッド署名にこのインタフェースがあればinterfacesリストに追加する. 現在入力されているTargetのインタフェースに@Interceptors注釈で定義されたインタフェースがある場合、生成エージェントは、そうでなければ元のtargetに戻る.
エージェントの実装エージェント実装後のExecutorなどは,そのメソッドを呼び出すとPluginのinvoke()メソッドを先に呼び出し,そのメソッドがエージェントされる必要があるかどうかを問い合わせる.言い換えれば,Executor,ParameterHandler,ResultSet Handler,StatementHandlerであり,@Intercepts注記ではどのメソッドの署名をブロックするかを定義する. 現在呼び出されているメソッドのメソッド署名がメソッド署名セットにおいてエージェントが必要であることを示す場合、ブロッキングのintercept()メソッドが呼び出される.そうでなければ、エージェントを必要とせずに元の方法を実行します. ブロッカーの論理実装インターセプト()メソッドにおける
(3)まとめプラグインメカニズムの実現は主に以下のステップに分けられる. コンフィギュレーション初期化中にPluginsタグを解析してブロックチェーンInterceptorChain に加える. SQLの実行中にExecutor、ParameterHandlerが初期化されたときpluginAll()メソッドを呼び出して対応するエージェントクラスを生成します. Executor、ParameterHandlerなどのクラスのメソッドを実行する場合、対応するエージェントクラスのメソッドが実行されます.
pluginAll()メソッドを呼び出してエージェントクラスを生成すると、ブロックチェーンリスト内のすべてのブロックを遍歴し、レイヤネストしてエージェントクラスを生成します.プロファイル内の下のプラグイン構成ほど、ネストされたエージェントクラスの外側にあります. 階層のエージェントはシステムのパフォーマンスに影響を与えるため、不要なエージェントを減らす必要があります.
MyBatisは、プラグインメカニズムをカスタマイズするための拡張を提供し、本質的にはブロッカーの機能を実現しています.
個人ホームページ:tuzhenyu’s page原文アドレス:MyBatis(五)—MyBatisのプラグインメカニズムを深く理解する
(0)カードメカニズム
(1)カードの使用
@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
try {
return invocation.proceed();
}finally {
long end = System.currentTimeMillis();
System.out.println("cost time:"+(end-start));
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties) {
}
}
<plugins>
<plugin interceptor="mybatis.interceptor.ExamplePlugin">plugin>
plugins>
(2)プラグインメカニズムの実現プロセス
1.プラグイン構成の初期化
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
public class InterceptorChain {
private final List interceptors = new ArrayList();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
2.ブロッキングの実行フロー
Executorのブロッカーは、sqlSession生成フェーズでExecutorインスタンスを作成するときにpluginAll()メソッドでブロッカーを追加します.
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
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);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
ParameterHandler,ResultSet Handler,StatamentHandlerブロッキングはSQLの特定の実行フェーズでpluginAll()メソッドでブロッキングを追加します.
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;
}
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;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
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 Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
public static Object wrap(Object target, Interceptor interceptor) {
Map, Set> signatureMap = getSignatureMap(interceptor);
Class> type = target.getClass();
Class>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
{interface org.apache.ibatis.executor.statement.StatementHandler=[public abstract int org.apache.ibatis.executor.statement.StatementHandler.update(java.sql.
Statement) throws java.sql.SQLException, public abstract java.util.List org.apache.ibatis.executor.statement.StatementHandler.query(java.sql.Statement,org.apache.
ibatis.session.ResultHandler) throws java.sql.SQLException]}
private static Map<Class>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class>, Set<Method>> signatureMap = new HashMap<Class>, Set<Method>>();
for (Signature sig : sigs) {
Set methods = signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet();
signatureMap.put(sig.type(), methods);
}
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
private static Class>[] getAllInterfaces(Class> type, Map<Class>, Set<Method>> signatureMap) {
Set> interfaces = new HashSet>();
while (type != null) {
for (Class> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class>[interfaces.size()]);
}
エージェントの実装
@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)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
try {
return invocation.proceed();
}finally {
long end = System.currentTimeMillis();
System.out.println("cost time:"+(end-start));
}
}
(3)まとめ