各種ソースコードにおける責任チェーンモデルの概要

10615 ワード

文書ディレクトリ
  • 一、AOPにおける責任チェーン
  • 二、Spring MVCにおけるブロッキングチェーン
  • 三、サーブレット中のFilter
  • 四、Mybatisプラグインのブロックチェーン
  • 五、NettyのChannelPipeline
  • 六、TomcatのPipeline-Valve
  • 一、AOP中の責任チェーン
    AOPの責任チェーンは、すべての要素を1つのチェーンオブジェクトにカプセル化して記録し、チェーンオブジェクトのinvokeメソッドを呼び出し、チェーンノードにチェーンを渡し、チェーンノードがチェーンを下に進むかどうかを制御します.
  • まずJdkDynamicAopProxyクラスにおけるinvokeメソッド
  • を見る.
    // Get the interception chain for this method.
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    
    // Check whether we have any advice. If we don't, we can fallback on direct
    // reflective invocation of the target, and avoid creating a MethodInvocation.
    if (chain.isEmpty()) {
    	// We can skip creating a MethodInvocation: just invoke the target directly
    	// Note that the final invoker must be an InvokerInterceptor so we know it does
    	// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
    	Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    	retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }
    //       ,      proceed  。
    else {
    	// We need to create a method invocation...
    	invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    	// Proceed to the joinpoint through the interceptor chain.
    	retVal = invocation.proceed();
    }
    
  • それからreflectiveMethodInvocationクラスに着いて、このクラスは私たちのいわゆるチェーンです.
  • @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によって管理されます.