各種ソースコードにおける責任チェーンモデルの概要
10615 ワード
文書ディレクトリ一、AOPにおける責任チェーン 二、Spring MVCにおけるブロッキングチェーン 三、サーブレット中のFilter 四、Mybatisプラグインのブロックチェーン 五、NettyのChannelPipeline 六、TomcatのPipeline-Valve 一、AOP中の責任チェーン
AOPの責任チェーンは、すべての要素を1つのチェーンオブジェクトにカプセル化して記録し、チェーンオブジェクトのinvokeメソッドを呼び出し、チェーンノードにチェーンを渡し、チェーンノードがチェーンを下に進むかどうかを制御します.まずJdkDynamicAopProxyクラスにおけるinvokeメソッド を見る.それからreflectiveMethodInvocationクラスに着いて、このクラスは私たちのいわゆるチェーンです.
現在実行されているチェーンノードを下付きインデックスで記録し、thisを対応するノードに渡します.次に、対応ノードの実装を見てみましょう.Adviceには多くの種類があります.ここでは@AfterReturning対応のAdviceを紹介します.これらはMethodInterceptorメソッドを実現しています.
以上よりSpringにおけるAOPの責任チェーン方式はチェーン+ノード方式を採用している.ReflectiveMethodInvocationはチェーンであり、ノードはMethodInterceptorインタフェースを実現したオブジェクトであり、チェーン(またはチェーンという制御権)を対応するチェーンノードに渡すことで、各ノードでチェーンが下に進むか、ブロックするかなどを制御することができる.
二、Spring MVCにおけるブロッキングチェーン
SpringMvcで要求を対応するHandlerにマッピングした場合,責任チェーン方式で要求をブロック処理した.具体的には、AbstractHandlerMappingクラスのgethandlerメソッドでプロセッサチェーンHandlerExecutionChainが返されます.
その後、HandlerExecutionChainには多くのHandlerInterceptor+handlerが含まれ、applyPrehandleメソッドを呼び出すことで責任チェーンがトリガーされます.
小結:ここでは、現在実行中のノードの位置を下付きで変更し、trueまたはfalseを返すことで、下向きに実行するかどうかを決定します.ここではAOPのようにチェーンをノードに伝達する方法はない.AOPの責任チェーン全体の呼び出しには戻り値があるが,ここでの責任チェーン呼び出しには戻り値がないため,要求応答オブジェクトはパラメータを介してコンテキストとして責任チェーンのノードに伝達される.
三、サーブレットの中のFilter
Filterはサーブレットの前に機能し,上記のHandlerInterceptorとは異なる要求と応答を前処理することができる.Filterはサーブレットに到達するとフィルタ処理され、Filterはサーブレット仕様で定義され、サーブレットコンテナでサポートされています.インターセプタはSpringコンテナ内でSpringによって管理されており、Springのコンポーネントであり、Springによって管理され、Springファイルに配置されているため、Spring内の任意のリソース、オブジェクトなどを使用することができ、Filterはできません.
一般的な方法はCompositeFilterであり、組合せモードを採用し、外部に1つのFilterを露出し、内部に1つのFilterチェーンテーブルを組み合わせ、その後、1つずつ実行する.ここでのdoFilterメソッドは戻り値がなく,要求をブロックせず,1つずつ下に伝えるだけで簡単である.これは要求をブロックするのではなく、前処理されます.
四、Mybatisプラグインのブロックチェーン
org.apache.ibatis.sessionパッケージには、Exetucorを生成するためにnewExecutorを使用する場合にInterceptorChainプロパティが使用され、InterceptorChainには注入されたすべてのインターセプタInterceptorが記録されているクラスConfigurationがあります.エージェントのExecutorを生成するために使用されます.
上のコードから分かるように、executor=(Executor)interceptorChain.pluginAll(executor);この行のコードは、ブロッキングチェーンがエージェントのexecutorを生成し、InterceptorChainの実装を見て簡単です.
ここでは,前の責任チェーンとはやや異なり,エージェント方式を用いて,階層エージェント方式により機能の拡張を実現する.
一般的な方法は静的方法Pluginを用いる.wrapはJDKダイナミックエージェントを作成します.
mybatisプラグインのブロッキングチェーンは、チェーン上の各ブロッキングをエージェントジェネレータとして機能させることで、前の2つに比べて複雑です.このような方式の拡張性がより強く,最終的にどのようなエージェントオブジェクトを返すか,ブロックをどのようにトリガするかのintercept法は完全に我々自身で決定する.
五、Nettyの中のChannelPipeline
ChannelPipeline自身がチェーンテーブルのようなadd,addFirst,removeなどの操作を多く定義していることがわかり、デフォルト実装DefaultChannelPipelineでの構造関数は双方向チェーンテーブルであることがわかります.このチェーン自体は、各ノードがプロセッサである新規および削除操作をサポートします.
六、Tomcatの中のPipeline-Valve
このPipelineとValveモードでは、各Valveは次のValveの位置を感知することができ、5のようにチェーンテーブルのモードに似ている.ValveにはgetNextメソッドとinvokeメソッドがあります.これによりどのValveから実行してもよい.通常、Valve間の関係はPipelineによって管理されます.
AOPの責任チェーンは、すべての要素を1つのチェーンオブジェクトにカプセル化して記録し、チェーンオブジェクトのinvokeメソッドを呼び出し、チェーンノードにチェーンを渡し、チェーンノードがチェーンを下に進むかどうかを制御します.
// Get the interception chain for this method.
List
@Override
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
// , Advice
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
現在実行されているチェーンノードを下付きインデックスで記録し、thisを対応するノードに渡します.次に、対応ノードの実装を見てみましょう.Adviceには多くの種類があります.ここでは@AfterReturning対応のAdviceを紹介します.これらはMethodInterceptorメソッドを実現しています.
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
private final AfterReturningAdvice advice;
/**
* Create a new AfterReturningAdviceInterceptor for the given advice.
* @param advice the AfterReturningAdvice to wrap
*/
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//
Object retVal = mi.proceed();
//
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}
以上よりSpringにおけるAOPの責任チェーン方式はチェーン+ノード方式を採用している.ReflectiveMethodInvocationはチェーンであり、ノードはMethodInterceptorインタフェースを実現したオブジェクトであり、チェーン(またはチェーンという制御権)を対応するチェーンノードに渡すことで、各ノードでチェーンが下に進むか、ブロックするかなどを制御することができる.
二、Spring MVCにおけるブロッキングチェーン
SpringMvcで要求を対応するHandlerにマッピングした場合,責任チェーン方式で要求をブロック処理した.具体的には、AbstractHandlerMappingクラスのgethandlerメソッドでプロセッサチェーンHandlerExecutionChainが返されます.
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
その後、HandlerExecutionChainには多くのHandlerInterceptor+handlerが含まれ、applyPrehandleメソッドを呼び出すことで責任チェーンがトリガーされます.
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
小結:ここでは、現在実行中のノードの位置を下付きで変更し、trueまたはfalseを返すことで、下向きに実行するかどうかを決定します.ここではAOPのようにチェーンをノードに伝達する方法はない.AOPの責任チェーン全体の呼び出しには戻り値があるが,ここでの責任チェーン呼び出しには戻り値がないため,要求応答オブジェクトはパラメータを介してコンテキストとして責任チェーンのノードに伝達される.
三、サーブレットの中のFilter
Filterはサーブレットの前に機能し,上記のHandlerInterceptorとは異なる要求と応答を前処理することができる.Filterはサーブレットに到達するとフィルタ処理され、Filterはサーブレット仕様で定義され、サーブレットコンテナでサポートされています.インターセプタはSpringコンテナ内でSpringによって管理されており、Springのコンポーネントであり、Springによって管理され、Springファイルに配置されているため、Spring内の任意のリソース、オブジェクトなどを使用することができ、Filterはできません.
一般的な方法はCompositeFilterであり、組合せモードを採用し、外部に1つのFilterを露出し、内部に1つのFilterチェーンテーブルを組み合わせ、その後、1つずつ実行する.ここでのdoFilterメソッドは戻り値がなく,要求をブロックせず,1つずつ下に伝えるだけで簡単である.これは要求をブロックするのではなく、前処理されます.
四、Mybatisプラグインのブロックチェーン
org.apache.ibatis.sessionパッケージには、Exetucorを生成するためにnewExecutorを使用する場合にInterceptorChainプロパティが使用され、InterceptorChainには注入されたすべてのインターセプタInterceptorが記録されているクラスConfigurationがあります.エージェントのExecutorを生成するために使用されます.
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 = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
上のコードから分かるように、executor=(Executor)interceptorChain.pluginAll(executor);この行のコードは、ブロッキングチェーンがエージェントのexecutorを生成し、InterceptorChainの実装を見て簡単です.
public class InterceptorChain {
private final List interceptors = new ArrayList();
// Interceptor, plugin, 。
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);
}
}
ここでは,前の責任チェーンとはやや異なり,エージェント方式を用いて,階層エージェント方式により機能の拡張を実現する.
public interface Interceptor {
//
Object intercept(Invocation invocation) throws Throwable;
// , 。
Object plugin(Object target);
void setProperties(Properties properties);
}
一般的な方法は静的方法Pluginを用いる.wrapはJDKダイナミックエージェントを作成します.
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;
}
// , intercept 。
@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);
}
}
mybatisプラグインのブロッキングチェーンは、チェーン上の各ブロッキングをエージェントジェネレータとして機能させることで、前の2つに比べて複雑です.このような方式の拡張性がより強く,最終的にどのようなエージェントオブジェクトを返すか,ブロックをどのようにトリガするかのintercept法は完全に我々自身で決定する.
五、Nettyの中のChannelPipeline
ChannelPipeline自身がチェーンテーブルのようなadd,addFirst,removeなどの操作を多く定義していることがわかり、デフォルト実装DefaultChannelPipelineでの構造関数は双方向チェーンテーブルであることがわかります.このチェーン自体は、各ノードがプロセッサである新規および削除操作をサポートします.
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
六、Tomcatの中のPipeline-Valve
このPipelineとValveモードでは、各Valveは次のValveの位置を感知することができ、5のようにチェーンテーブルのモードに似ている.ValveにはgetNextメソッドとinvokeメソッドがあります.これによりどのValveから実行してもよい.通常、Valve間の関係はPipelineによって管理されます.