责任链模式:也叫职责链模式。是一种行为型设计模式,它将请求的发送者和接收者解耦,使多个对象都有机会处理请求。请求沿着一个链传递,直到有一个对象能够处理它为止。

在责任链模式中,通常会有一个抽象处理者(Handler)作为链条的基础,每个具体处理者(ConcreteHandler)都继承自抽象处理者,并且持有下一个处理者的引用。当一个请求到达时,它会首先被发送给链条的第一个处理者,如果该处理者能够处理请求,就处理它;否则,它会将请求传递给下一个处理者,直到有一个处理者能够处理请求或者请求到达链条的末尾。

用图表示如下:

责任链模式的优点:

  • 解耦请求发送者和接收者:请求发送者不需要知道具体的接收者,而是将请求发送给链条的第一个处理者,由处理者自行决定是否能够处理请求。

  • 可扩展性:可以灵活地增加、修改或重新排列处理者,以满足不同的需求,而无需修改客户端代码。

  • 动态组合:可以动态地组合处理者,根据实际情况灵活地创建责任链。

责任链模式的缺点:

  • 请求可能无法被处理:如果没有处理者能够处理请求,那么请求可能会到达链条的末尾而无法得到处理。

  • 性能考虑:由于请求需要在链条中传递,如果链条过长或者处理者的处理逻辑复杂,可能会影响性能。

  • 可能导致系统难以调试:由于请求的处理过程是动态的,可能会导致难以跟踪和调试问题。

责任链模式的主要角色如下。

  1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

  2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式的实现

// 抽象处理者角色
abstract class Handler {
    private Handler next;
    public void setNext(Handler next) {
        this.next = next;
    }
    public Handler getNext() {
        return next;
    }
    //处理请求的方法
    public abstract void handleRequest(String request);
}

// 具体处理者角色1
class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("one")) {
            System.out.println("具体处理者1负责处理该请求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("没有人处理该请求!");
            }
        }
    }
}

// 具体处理者角色2
class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("two")) {
            System.out.println("具体处理者2负责处理该请求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("没有人处理该请求!");
            }
        }
    }
}

// 测试类
public class ChainOfResponsibilityTest {
    public static void main(String[] args) {
        //组装责任链
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);
        //提交请求
        handler1.handleRequest("two");
    }
}

程序运行结果如下:

具体处理者2负责处理该请求!

责任链模式在Mybatis中的应用

package org.apache.ibatis.session;

public class Configuration {
    
    protected final InterceptorChain interceptorChain = new InterceptorChain();   
    
    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 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;
    }
    
    public Executor newExecutor(Transaction transaction) {
      return newExecutor(transaction, defaultExecutorType);
    }
    
    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;
    }
    
} 

插件是用来包装四大对象,拦截指定的方法:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

  • ParameterHandler (getParameterObject, setParameters)

  • ResultSetHandler (handleResultSets, handleOutputParameters)

  • StatementHandler (prepare, parameterize, batch, update, query)

package org.apache.ibatis.plugin;

public class InterceptorChain {

  private final List<Interceptor> 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<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  default void setProperties(Properties properties) {
    // NOP
  }

}

public class Plugin implements InvocationHandler {
    
    private final Object target;
    private final Interceptor interceptor;
    private final Map<Class<?>, Set<Method>> signatureMap;
    
    private Plugin(Object target, Interceptor interceptor, Map<Class<?>, 
        Set<Method>> signatureMap) {
      this.target = target;
      this.interceptor = interceptor;
      this.signatureMap = signatureMap;
    }
    
    public static Object wrap(Object target, Interceptor interceptor) {
      Map<Class<?>, Set<Method>> 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;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {
      try {
        Set<Method> 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);
      }
    }
    
}

责任链模式在Spring Aop中的应用

package org.springframework.aop.framework;

class CglibAopProxy implements AopProxy, Serializable {
    
    /** The configuration used to configure this proxy. */
    protected final AdvisedSupport advised; 
    
    public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
       Assert.notNull(config, "AdvisedSupport must not be null");
       if (config.getAdvisorCount() == 0 && config.getTargetSource() 
           == AdvisedSupport.EMPTY_TARGET_SOURCE) {
          throw new AopConfigException("...");
       }
       this.advised = config;
       this.advisedDispatcher = new AdvisedDispatcher(this.advised);
    }
    
    private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
     
       ……
    
       // Choose an "aop" interceptor (used for AOP calls).
       Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
    
       ……
    
       Callback[] mainCallbacks = new Callback[] {
             aopInterceptor, 
             targetInterceptor,  
             new SerializableNoOp(),  
             targetDispatcher, this.advisedDispatcher,
             new EqualsInterceptor(this.advised),
             new HashCodeInterceptor(this.advised)
       };
    
       Callback[] callbacks;
    
       if (isStatic && isFrozen) {
          ……
       }
       else {
          callbacks = mainCallbacks;
       }
       return callbacks;
    }
    
    private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

       private final AdvisedSupport advised;
    
       public DynamicAdvisedInterceptor(AdvisedSupport advised) {
          this.advised = advised;
       }
    
       @Override
       @Nullable
       public Object intercept(Object proxy, Method method, Object[] args, 
           MethodProxy methodProxy) throws Throwable {
          Object oldProxy = null;
          boolean setProxyContext = false;
          Object target = null;
          TargetSource targetSource = this.advised.getTargetSource();
          try {
             if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
             }
             // Get as late as possible to minimize the time we "own" the 
             // target, in case it comes from a pool...
             target = targetSource.getTarget();
             Class<?> targetClass = (target != null ? 
                 target.getClass() : null);
             List<Object> chain = this.advised
                 .getInterceptorsAndDynamicInterceptionAdvice(method, 
                 targetClass);
             Object retVal;
             // Check whether we only have one InvokerInterceptor: that is,
             // no real advice, but just reflective invocation of the 
             // target.
             if (chain.isEmpty() && Modifier.isPublic(
                 method.getModifiers())) {
                // 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 = methodProxy.invoke(target, argsToUse);
             }
             else {
                // We need to create a method invocation...
                retVal = new CglibMethodInvocation(proxy, target, method, 
                    args, targetClass, chain, methodProxy).proceed();
             }
             retVal = processReturnType(proxy, target, method, retVal);
             return retVal;
          }
          finally {
             if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
             }
             if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
             }
          }
       }
    }
    
    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

       @Nullable
       private final MethodProxy methodProxy;
    
       public CglibMethodInvocation(Object proxy, 
             @Nullable Object target, 
             Method method, Object[] arguments, 
             @Nullable Class<?> targetClass,
             List<Object> interceptorsAndDynamicMethodMatchers, 
             MethodProxy methodProxy) {
    
          super(proxy, target, method, arguments, targetClass, 
              interceptorsAndDynamicMethodMatchers);
    
          // Only use method proxy for public methods not derived from 
          // java.lang.Object
          this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
                method.getDeclaringClass() != Object.class && 
                !AopUtils.isEqualsMethod(method) &&
                !AopUtils.isHashCodeMethod(method) && 
                !AopUtils.isToStringMethod(method) ?
                methodProxy : null);
       }
    
       @Override
       @Nullable
       public Object proceed() throws Throwable {
          try {
             return super.proceed();
          }
          catch (RuntimeException ex) {
             throw ex;
          }
          catch (Exception ex) {
             if (ReflectionUtils.declaresException(getMethod(), 
                     ex.getClass())) {
                throw ex;
             }
             else {
                throw new UndeclaredThrowableException(ex);
             }
          }
       }
    
       /**
        * Gives a marginal performance improvement versus using 
        * reflection to invoke the target when invoking public methods.
        */
       @Override
       protected Object invokeJoinpoint() throws Throwable {
          if (this.methodProxy != null) {
             return this.methodProxy.invoke(this.target, this.arguments);
          }
          else {
             return super.invokeJoinpoint();
          }
       }
    }
    
}

public class AdvisedSupport extends ProxyConfig implements Advised {
    
    AdvisorChainFactory advisorChainFactory = 
        new DefaultAdvisorChainFactory();
    
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
        Method method, @Nullable Class<?> targetClass) {
       MethodCacheKey cacheKey = new MethodCacheKey(method);
       List<Object> cached = this.methodCache.get(cacheKey);
       if (cached == null) {
          cached = this.advisorChainFactory
              .getInterceptorsAndDynamicInterceptionAdvice(
              this, method, targetClass);
          this.methodCache.put(cacheKey, cached);
       }
       return cached;
    }
    
}

public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
        
    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
          Advised config, Method method, @Nullable Class<?> targetClass) {
    
       // This is somewhat tricky... We have to process introductions 
       // first, but we need to preserve order in the ultimate list.
       AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry
           .getInstance();
       Advisor[] advisors = config.getAdvisors();
       List<Object> interceptorList = new ArrayList<>(advisors.length);
       Class<?> actualClass = (targetClass != null ? 
           targetClass : method.getDeclaringClass());
       Boolean hasIntroductions = null;
    
       for (Advisor advisor : advisors) {
          if (advisor instanceof PointcutAdvisor) {
             // Add it conditionally.
             PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
             if (config.isPreFiltered() || pointcutAdvisor.getPointcut()
                     .getClassFilter().matches(actualClass)) {
                MethodMatcher mm = pointcutAdvisor.getPointcut()
                    .getMethodMatcher();
                boolean match;
                if (mm instanceof IntroductionAwareMethodMatcher) {
                   if (hasIntroductions == null) {
                      hasIntroductions = hasMatchingIntroductions(
                          advisors, actualClass);
                   }
                   match = ((IntroductionAwareMethodMatcher) mm)
                       .matches(method, actualClass, hasIntroductions);
                }
                else {
                   match = mm.matches(method, actualClass);
                }
                if (match) {
                   MethodInterceptor[] interceptors = registry
                       .getInterceptors(advisor);
                   if (mm.isRuntime()) {
                      // Creating a new object instance in the 
                      // getInterceptors() method isn't a problem 
                      // as we normally cache created chains.
                      for (MethodInterceptor interceptor : interceptors) {
                         interceptorList.add(
                         new InterceptorAndDynamicMethodMatcher(
                         interceptor, mm));
                      }
                   }
                   else {
                      interceptorList.addAll(Arrays.asList(interceptors));
                   }
                }
             }
          }
          else if (advisor instanceof IntroductionAdvisor) {
             IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
             if (config.isPreFiltered() || ia.getClassFilter().matches(
                     actualClass)) {
                Interceptor[] interceptors = registry.getInterceptors(
                    advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
             }
          }
          else {
             Interceptor[] interceptors = registry.getInterceptors(
                 advisor);
             interceptorList.addAll(Arrays.asList(interceptors));
          }
       }
    
       return interceptorList;
    }    
        
}

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
        
    @Override
    @Nullable
    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;
          Class<?> targetClass = (this.targetClass != null ? 
              this.targetClass : this.method.getDeclaringClass());
          if (dm.methodMatcher.matches(this.method, targetClass, 
                  this.arguments)) {
             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);
       }
    }    
        
}  

@Before、@After、@Around注解分别对应AspectJMethodBeforeAdvice、AspectJAfterAdvice、AspectJAroundAdvice这三个增强器。

package org.springframework.aop.aspectj;

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
            
    @Override
    public void before(Method method, Object[] args, 
            @Nullable Object target) throws Throwable {
       invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
    
    @Override
    public boolean isBeforeAdvice() {
       return true;
    }
    
    @Override
    public boolean isAfterAdvice() {
       return false;
    }         
            
}
            
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      try {
         return mi.proceed();
      }
      finally {
         invokeAdviceMethod(getJoinPointMatch(), null, null);
      }
   }

   @Override
   public boolean isBeforeAdvice() {
      return false;
   }

   @Override
   public boolean isAfterAdvice() {
      return true;
   }

}      

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {

   @Override
   public boolean isBeforeAdvice() {
      return false;
   }

   @Override
   public boolean isAfterAdvice() {
      return false;
   }

   @Override
   protected boolean supportsProceedingJoinPoint() {
      return true;
   }

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      if (!(mi instanceof ProxyMethodInvocation)) {
         throw new IllegalStateException("......");
      }
      ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
      ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
      JoinPointMatch jpm = getJoinPointMatch(pmi);
      return invokeAdviceMethod(pjp, jpm, null, null);
   }

   protected ProceedingJoinPoint lazyGetProceedingJoinPoint(
           ProxyMethodInvocation rmi) {
      return new MethodInvocationProceedingJoinPoint(rmi);
   }

}

public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice, Serializable {

   @Override
   public boolean isBeforeAdvice() {
      return false;
   }

   @Override
   public boolean isAfterAdvice() {
      return true;
   }

   @Override
   public void setReturningName(String name) {
      setReturningNameNoCheck(name);
   }

   @Override
   public void afterReturning(@Nullable Object returnValue, Method method,
   Object[] args, @Nullable Object target) throws Throwable {
      if (shouldInvokeOnReturnValueOf(method, returnValue)) {
         invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
      }
   }

   private boolean shouldInvokeOnReturnValueOf(Method method, 
           @Nullable Object returnValue) {
      Class<?> type = getDiscoveredReturningType();
      Type genericType = getDiscoveredReturningGenericType();
      // If we aren't dealing with a raw type, check if generic 
      // parameters are assignable.
      return (matchesReturnValue(type, method, returnValue) &&
            (genericType == null || genericType == type ||
                  TypeUtils.isAssignable(genericType, 
                      method.getGenericReturnType())));
   }

   private boolean matchesReturnValue(Class<?> type, Method method, 
           @Nullable Object returnValue) {
      if (returnValue != null) {
         return ClassUtils.isAssignableValue(type, returnValue);
      }
      else if (Object.class == type && void.class == method
              .getReturnType()) {
         return true;
      }
      else {
         return ClassUtils.isAssignable(type, method.getReturnType());
      }
   }

}

public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {

   @Override
   public boolean isBeforeAdvice() {
      return false;
   }

   @Override
   public boolean isAfterAdvice() {
      return true;
   }

   @Override
   public void setThrowingName(String name) {
      setThrowingNameNoCheck(name);
   }

   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      try {
         return mi.proceed();
      }
      catch (Throwable ex) {
         if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
         }
         throw ex;
      }
   }

   private boolean shouldInvokeOnThrowing(Throwable ex) {
      return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());
   }

}
文章作者: Hakurei
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Zero
设计模式 设计模式 Java
喜欢就支持一下吧