设计模式之责任链模式(结合OKHttp的拦截器学习)

1,518 阅读5分钟

前言

   责任链模式,顾名思义,就是由一个个负有一定责任的单元形成的一个链条,在这个链条上,每个
责任单元都负责自己应该负责的责任,而责任单元之间时互不干扰的,当有事件需要处理时,从链条的
首个责任单元开始处理,首个责任单元处理事件中自己负责的部分,当处理完之后,若事件还未处理完
毕,还需进一步处理而同时当前责任单元无法处理或是不是自己负责的部分时,当前责任单元将事件传
递给下一个责任单元,而后面该哪个责任单元处理,当前责任单元不关心,当前责任单元只需处理自己
负责的部分并确定事件是否该继续传递到下一责任单元。

举例

责任链模式例子视图.png

Android开发中用到的责任链模式

相信大家在Android开发过程中都用到过okhttp或是Retrofit网络请求框架,而okhttp中就是使用到了责任链设计模式,即便使用的时retrofit,而retrofit也只是封装的okhttp。okhttp中的拦截器使用的就是责任链设计模式,相信大家都会有用到过这其中的拦截器去处理网络请求,比如对Cookie的处理。下面将从okhttp拦截器的实现源码角度学习责任链设计模式。

1. 拦截器的使用

如需对cookie进行处理,我们一般会定义一个拦截器类继承自Interceptor,并重写intercept方法,在该方法中处理cookie(添加或获取cookie保存),以下代码实现的是向请求头加入cookie的拦截器,获取请求头中cookie方法与此类似,这里不做展示。

/**
 * 定义OkHttp3拦截器,处理数据包的header,向header种添加cookie
 */
public class InterceptorOfAddCookie implements Interceptor {

    private static final String TAG = "InterceptorOfAddCookie";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Log.d(TAG, "InterceptorOfAddCookie");
        return chain.proceed(chain.request());
    }
}

接着向okhttpClient对象中添加拦截器,使用的方法如下面的addInterceptor方法,参数就是创建的拦截器类对象,我这里是添加了两个拦截器,包括cookie的添加和获取。

okHttpClient = new OkHttpClient
        .Builder()
        .addInterceptor(new InterceptorOfAddCookie())
        .addInterceptor(new InterceptorOfReceivedCookie())
        .build();

正式进入正题,切入点就在这里的addInterceptor方法,查看一下该方法的源码,看一下内部实现了怎样的逻辑处理。

/**
 * 通过addInterceptor方法将自定义的cookie处理拦截器添加到这的interceptor
 * 中,在源码中可以看到interceptors其实就是一个List集合,即拦截器集合,而这里
 * 的拦截器集合就可以看作是我们这次责任链模式中的责任链,集合中的每一个拦截器就
 * 相当于之前所说的责任单元。
 */
public Builder addInterceptor(Interceptor interceptor) {
  if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
  interceptors.add(interceptor);
  return this;
}

然后是在再看到ohhttpClient中使用拦截器并发送请求的过程

okHttpClient = new OkHttpClient
        .Builder()
        .addInterceptor(new InterceptorOfAddCookie())
        .addInterceptor(new InterceptorOfReceivedCookie())
        .build();

Request request = new Request.Builder().url("").get().build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

    }
});

其中拦截器是被添加到了okhttpClient的拦截器集合interceptors中,而通过okHttpClient.newCall(request)方法将okhttpClient引用到了RealCall中的client, 因为在okHttpClient.newCall()方法源码如下:

@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

可以看到newCall方法实际上创建的是RealCall对象,RealCall类实现了Call方法。接着再到call对象调用enqueue(CallBack callBack)方法发起请求,进入到enqueue内部查看,即进入到RealCall中的enqueue()方法中:

@Override public void enqueue(Callback responseCallback) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  transmitter.callStart();
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
可以看到这边创建了一个AsyncCall对象,并传入CallBack对象,在RealCall类中可以找到合格内部类AsyncCall是继承自NamedRunnable,而进一步查看NamedRunnable是继承自
Runnable,所以AsyncCall可以被看作为一个Runnable

沿着client.dispatcher().enqueue(new AsyncCall(responseCallback));方法进入到Dispatcher类中的enqueue方法中,

void enqueue(AsyncCall call) {
  synchronized (this) {
    readyAsyncCalls.add(call);

    // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
    // the same host.
    if (!call.get().forWebSocket) {
      AsyncCall existingCall = findExistingCallWithHost(call.host());
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
    }
  }
  promoteAndExecute();
}

可以发现这里最终调用了promoterAndExecute()方法,再看一下这个方法中具体实现

private boolean promoteAndExecute() {
  assert (!Thread.holdsLock(this));

  List<AsyncCall> executableCalls = new ArrayList<>();
  boolean isRunning;
  synchronized (this) {
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall asyncCall = i.next();

      if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
      if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

      i.remove();
      asyncCall.callsPerHost().incrementAndGet();
      executableCalls.add(asyncCall);
      runningAsyncCalls.add(asyncCall);
    }
    isRunning = runningCallsCount() > 0;
  }

  for (int i = 0, size = executableCalls.size(); i < size; i++) {
    AsyncCall asyncCall = executableCalls.get(i);
    asyncCall.executeOn(executorService());
  }

  return isRunning;
}

可以发现在这个方法最终会调用 asyncCall.executeOn(executorService());这里的executeOn传入的参为线程池对象 ExecutorService实例,在回到AsyncCall类中查看executeOn方法的具体实现,

void executeOn(ExecutorService executorService) {
  assert (!Thread.holdsLock(client.dispatcher()));
  boolean success = false;
  try {
    executorService.execute(this);
    success = true;
  } catch (RejectedExecutionException e) {
    InterruptedIOException ioException = new InterruptedIOException("executor rejected");
    ioException.initCause(e);
    transmitter.noMoreExchanges(ioException);
    responseCallback.onFailure(RealCall.this, ioException);
  } finally {
    if (!success) {
      client.dispatcher().finished(this); // This call is no longer running!
    }
  }
}

可以看到executorService.execute(this);就是将this(即AsyCall对象,而AsyncCall之前提到它
的父类NameRunnable是实现了Runnable的)传入到线程池中,当线程池执行该Runnable任务时回执行
run()方法,而可以看到AsyncCall父类NameRunnable类中的run()方法是调用了自身的execute()方
法,而在AsyncCall中重写了该execute()方法,即执行NameRunnable的execute()相当于执行了AsyncCall类中的execute()方法。

再看到execute()方法中,在这个方法中主要看到Response response = getResponseWithInterceptorChain();这一行,查看一下getResponseWithInterceptorChain()方法的实现:

Response getResponseWithInterceptorChain() throws IOException {
  // Build a full stack of interceptors.
  List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());
  interceptors.add(new RetryAndFollowUpInterceptor(client));
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  interceptors.add(new CacheInterceptor(client.internalCache()));
  interceptors.add(new ConnectInterceptor(client));
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  interceptors.add(new CallServerInterceptor(forWebSocket));

  Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
      originalRequest, this, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());

  boolean calledNoMoreExchanges = false;
  try {
    Response response = chain.proceed(originalRequest);
    if (transmitter.isCanceled()) {
      closeQuietly(response);
      throw new IOException("Canceled");
    }
    return response;
  } catch (IOException e) {
    calledNoMoreExchanges = true;
    throw transmitter.noMoreExchanges(e);
  } finally {
    if (!calledNoMoreExchanges) {
      transmitter.noMoreExchanges(null);
    }
  }
}

这里发现了创建了一个拦截器集合,并通过client.interceptors()方法获取到了client的拦截器集
合interceptors,随后也往新创建的拦截器集合添加了其他的拦截器,而client中的拦截器集合包含
的只是我们自定义的拦截器集合,还记得起初提到的创建okhttpClient实例时通过addInterceptor
方法添加自定义拦截器吗?所以在这里也可以发现,如果处理拦截器的时候会先执行我们自定义的拦截
器再执行内部的拦截器。

再往下看会发现Interceptor.Chain chain = new RealInterceptorChain()传入iterceptors创建
了Interceptor.Chain,这个就是责任链,将拦截器集合都放到这个链条上,组成了一个拦截器责任链。
注意:RealInterceptorChain实现了Interceptor接口中的内部接口Chain接口。

接着往下看Response response = chain.proceed(originalRequest);这里执行了chain的proceed方法并传入了Request对象originalRequest(即是我们最初创建
Call call = okHttpClient.newCall(request),RealCall对象)

接着再看chain.proceed方法的具体实现(进入到RealInterceptorChain类中,因为该类实现了Chain接口,所以具体逻辑实现会在该类的proceed中):

@Override public Response proceed(Request request) throws IOException {
  return proceed(request, transmitter, exchange);
}

其内部依然调用proceed方法,再看自身的proceed方法:
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
    throws IOException {
  if (index >= interceptors.size()) throw new AssertionError();

  calls++;

  // If we already have a stream, confirm that the incoming request will use it.
  if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must retain the same host and port");
  }

  // If we already have a stream, confirm that this is the only call to chain.proceed().
  if (this.exchange != null && calls > 1) {
    throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
  }

  // Call the next interceptor in the chain.
  RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
      index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);

  // Confirm that the next interceptor made its required call to chain.proceed().
  if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }

  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }

  if (response.body() == null) {
    throw new IllegalStateException(
        "interceptor " + interceptor + " returned a response with no body");
  }

  return response;
}

//其中最关键的代码在于这三行代码
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

这里又通过调用RealInterceptorChain类构造方法,而这里不同的是,参数index的值为index+1,并且在该类中index为全局变量,所以index的值增量为1,通过index将拦截器集合interceptors中的第index个拦截器interceptor取出,并执行interceptor的interceprt(Chain)方法,接着我们回顾一下最初我们自定义的拦截其中实现了什么逻辑:

public class InterceptorOfAddCookie implements Interceptor {

    private static final String TAG = "InterceptorOfAddCookie";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Log.d(TAG, "InterceptorOfAddCookie");
        return chain.proceed(chain.request());
    }
}

可以看到intercept(Chain)(这里的Chain为RealInterceptorChain)方法中调用了
chain.proceed(Request)方法,即又调用了proceed方法,而前面分析到RealInterceptorChain重
写父接口的proceed方法的具体实现中又调用了RealInterceptorChain自身的proceed方法,而自身
的proceed方法又调用了interceptor.intercept()方法,所以这里是形成了一个递归,而这里的递归
思想就是责任链模式的核心思想。即不断执行拦截器interceptor中的intercept(Chain)方法,而我
们只需要在intercept方法中实现我们的逻辑即可,可以通过Chain获取到Request或者Response,实现
对请求体或请求头的处理,如处理请求头的cookie。
总结

okhttp中的拦截器实现可以总结为如下:

责任单元(抽取为接口方便扩展)
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;
}

责任链
public final class Chain {

  private final List<Interceptor> interceptors;
  
  public RealInterceptorChain(List<Interceptor> interceptors, Transmitter transmitter,
        @Nullable Exchange exchange, int index, Request request, Call call,
        int connectTimeout, int readTimeout, int writeTimeout) {
      this.interceptors = interceptors;
      this.index = index;
      this.request = request;
      ....
    }

  @Override public Response proceed(Request) throws IOException {
    return proceed(Request,Transmitter,Exchange);
  }

  public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    
    RealInterceptorChain next = new RealInterceptorChain(interceptors,transmitter, exchange,
    index + 1, request, call, connectTimeout, readTimeout,   writeTimeout);
    //注意这里的index+1
    Interceptor interceptor = interceptors.get(index);
    interceptor.intercept(Chain);//这里的Chain通过构造方法创建
    return response;
  }
}

责任单元逻辑代码实现
public class InterceptorOfAddCookie implements Interceptor {

    private static final String TAG = "InterceptorOfAddCookie";

    @Override
    public Response intercept(Chain chain) throws IOException {
        /**
         *这边实现具体的逻辑处理
         */
        return chain.proceed(Request);
        //调用责任链中的proceed方法实现递归调用
        //这里的request通过在Chain中定义获取方法得到request对象
    }
}

这样的设计方法明显易于后续扩展,而不会涉及到期待责任单元的逻辑更改,只需创建一个类要实现责任单元接口,创建这个类的实例,并将其添加到责任链中即可。该设计模式的关键思想在于递归,在责任链Chain中通过递归调用责任单元方法,即可将要处理的事件沿着责任链传递处理,也可以在责任单元中通过逻辑判断是否要将事件继续传递到下一责任单元。