spring中怎么使用責(zé)任連模式

spring中怎么使用責(zé)任連模式,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:國(guó)際域名空間、虛擬主機(jī)、營(yíng)銷軟件、網(wǎng)站建設(shè)、衢江網(wǎng)站維護(hù)、網(wǎng)站推廣。

1. 外部控制模式

??對(duì)于外部控制的方式,這種方式比較簡(jiǎn)單,鏈的每個(gè)節(jié)點(diǎn)只需要專注于各自的邏輯即可,而當(dāng)前節(jié)點(diǎn)調(diào)用完成之后是否繼續(xù)調(diào)用下一個(gè)節(jié)點(diǎn),這個(gè)則由外部控制邏輯決定。這里我們以一個(gè)過(guò)濾器的實(shí)現(xiàn)邏輯進(jìn)行講解,在平常工作中,我們經(jīng)常需要根據(jù)一系列的條件對(duì)某個(gè)東西進(jìn)行過(guò)濾,比如任務(wù)服務(wù)的設(shè)計(jì),在執(zhí)行某個(gè)任務(wù),其需要經(jīng)過(guò)諸如時(shí)效性的檢驗(yàn),風(fēng)控?cái)r截,任務(wù)完成次數(shù)等的過(guò)濾條件的檢驗(yàn)之后才能判斷當(dāng)前任務(wù)是否能夠執(zhí)行,只有在所有的過(guò)濾條件都完成之后,我們才能執(zhí)行該任務(wù)。那么,這里我們可以抽象出一個(gè)Filter接口,設(shè)計(jì)如下:

public interface Filter {

  /**
   * 用于對(duì)各個(gè)任務(wù)節(jié)點(diǎn)進(jìn)行過(guò)濾
   */
  boolean filter(Task task);

}

??這里的Filter.filter方法只有一個(gè)參數(shù)Task,主要就是控制當(dāng)天task是否需要過(guò)濾掉,其中有個(gè)boolean類型的返回值,通過(guò)該返回值告知外部控制邏輯是否需要將該task過(guò)濾掉。對(duì)于該接口的子類,我們只需要將其聲明為spring所管理的一個(gè)bean即可:

@Component
public class DurationFilter implements Filter {

  @Override
  public boolean filter(Task task) {
    System.out.println("時(shí)效性檢驗(yàn)");
    return true;
  }
}
@compoment
public class Risk implements Filter {
    @override
    public boolean filter(Task task){
       System.out.println("風(fēng)控?cái)r截");
       return true;
    }
}
@Component
public class TimesFilter implements Filter {

  @Override
  public boolean filter(Task task) {
    System.out.println("次數(shù)限制檢驗(yàn)");
    return true;
  }
}

??上面我們模擬聲明了3個(gè)Filter的子類,用于設(shè)計(jì)一系列的控制當(dāng)天task是否需要被過(guò)濾的邏輯,結(jié)構(gòu)上的邏輯其實(shí)比較簡(jiǎn)單,主要就是需要將其聲明為spring所管理的一個(gè)bean。下面是我們的控制邏輯:

@Service
public class ApplicationService {

  @Autowired
  private List<Filter> filters;

  public void mockedClient() {
    Task task = new Task(); // 這里task一般是通過(guò)數(shù)據(jù)庫(kù)查詢得到的
    for (Filter filter : filters) {
      if (!filter.filter(task)) {
        return;
      }
    }

    // 過(guò)濾完成,后續(xù)是執(zhí)行任務(wù)的邏輯
  }
}

??上述的控制邏輯中,對(duì)于過(guò)濾器的獲取,只需要通過(guò)spring的自動(dòng)注入即可,這里的注入是一個(gè)List<Filter>,就是說(shuō),如果我們有新的Filter實(shí)例需要參與責(zé)任鏈的過(guò)濾,只需要聲明為一個(gè)Spring容器所管理的bean即可。
??這種責(zé)任鏈設(shè)計(jì)方式的優(yōu)點(diǎn)在于鏈的控制簡(jiǎn)單,只需要實(shí)現(xiàn)一個(gè)統(tǒng)一的接口即可,基本上滿足大部分的邏輯控制,但是對(duì)于某些動(dòng)態(tài)調(diào)整鏈的需求就無(wú)能為力了。比如在執(zhí)行到某個(gè)節(jié)點(diǎn)之后需要?jiǎng)討B(tài)的判斷師傅執(zhí)行下一個(gè)節(jié)點(diǎn),或者說(shuō)要執(zhí)行,某些分叉點(diǎn)的節(jié)點(diǎn)等。這個(gè)時(shí)候,我們就需要將鏈節(jié)點(diǎn)的傳遞工作交個(gè)各個(gè)節(jié)點(diǎn)執(zhí)行。

1. 外部控制模式
對(duì)于節(jié)點(diǎn)控制調(diào)用的方式,其主要有三個(gè)控制點(diǎn):Handler,HandlerContext和PipeLine。Handle是用于編寫(xiě)具體的業(yè)務(wù)代碼,HandlerContext用于對(duì)Handler進(jìn)行包裹,并且用于控制下一個(gè)節(jié)點(diǎn)的調(diào)用;PipeLine則主要是用于控制整體的流程調(diào)用的,比如對(duì)于任務(wù)的執(zhí)行,其有任務(wù)查詢,任務(wù)的過(guò)濾和執(zhí)行任務(wù)等等流程,這些流程整體的邏輯控制就是有pipeline控制,在每個(gè)流程中又包含了一些列的子流程,這些子流程則是由一個(gè)個(gè)的HandlerContext和Handler進(jìn)行梳理的,這種責(zé)任鏈的控制方式整體邏輯如下圖所示:

spring中怎么使用責(zé)任連模式![]
從上圖可以看出,我們整個(gè)流程通過(guò)pipeline對(duì)象進(jìn)行了抽象,這里主要分為了3個(gè)步驟:查詢task,過(guò)濾task和執(zhí)行task。在每個(gè)步驟中,我們都是用一系列的鏈?zhǔn)秸{(diào)用。途中需要注意的是,在每次調(diào)用鏈的下一個(gè)節(jié)點(diǎn)的時(shí)候,我們都是通過(guò)具體的Handler進(jìn)行的,也就是說(shuō)進(jìn)行鏈的下一個(gè)節(jié)點(diǎn)的調(diào)用,我們是通過(guò)業(yè)務(wù)實(shí)現(xiàn)方來(lái)進(jìn)行動(dòng)態(tài)的控制。

關(guān)于該模式的設(shè)計(jì),我們首先需要強(qiáng)調(diào)的就是Handler接口的設(shè)計(jì),其設(shè)計(jì)如下所示:

public interface Handler {

  /**
   * 處理接收到前端請(qǐng)求的邏輯
   */
  default void receiveTask(HandlerContext ctx, Request request) {
    ctx.fireTaskReceived(request);
  }

  /**
   * 查詢到task之后,進(jìn)行task過(guò)濾的邏輯
   */
  default void filterTask(HandlerContext ctx, Task task) {
    ctx.fireTaskFiltered(task);
  }

  /**
   * task過(guò)濾完成之后,處理執(zhí)行task的邏輯
   */
  default void executeTask(HandlerContext ctx, Task task) {
    ctx.fireTaskExecuted(task);
  }

  /**
   * 當(dāng)實(shí)現(xiàn)的前面的方法拋出異常時(shí),將使用當(dāng)前方法進(jìn)行異常處理,這樣可以將每個(gè)handler的異常
   * 都只在該handler內(nèi)進(jìn)行處理,而無(wú)需額外進(jìn)行捕獲
   */
  default void exceptionCaught(HandlerContext ctx, Throwable e) {
    throw new RuntimeException(e);
  }

  /**
   * 在整個(gè)流程中,保證最后一定會(huì)執(zhí)行的代碼,主要是用于一些清理工作
   */
  default void afterCompletion(HandlerContext ctx) {
    ctx.fireAfterCompletion(ctx);
  }
}

這里的Handler接口主要是對(duì)具體的業(yè)務(wù)邏輯的一個(gè)抽象,對(duì)于該Handler主要有如下幾點(diǎn)需要說(shuō)明:

  • 在前面圖中pipline的每個(gè)層級(jí)中對(duì)應(yīng)于改Handler都有一個(gè)方法,在需要進(jìn)行具體的業(yè)務(wù)處理的時(shí)候,用戶只需要聲明一個(gè)bean,具體實(shí)現(xiàn)某個(gè)業(yè)務(wù)所需要處理的層級(jí)的方法即可,無(wú)需管其他的邏輯;

  • 每個(gè)層級(jí)的方法都有默認(rèn)的實(shí)現(xiàn),默認(rèn)實(shí)現(xiàn)方式就是將鏈的調(diào)用繼續(xù)往下進(jìn)行傳遞

  • 每個(gè)層級(jí)的方法中,第一個(gè)參數(shù)都是一個(gè)Handler類型的,該參數(shù)主要是用于進(jìn)行流程控制的,比如是否需要將當(dāng)前層級(jí)的調(diào)用鏈繼續(xù)往下傳遞,這里的鏈的傳遞工作主要是通過(guò)ctx.filterXXX()方法進(jìn)行

  • 每個(gè)Handler中都有一個(gè)exceptionCaught()和afterCompletion()方法,這兩個(gè)方法分別用于異常的控制和所有調(diào)用完成之后的清理,這里的異??刂浦饕遣东@當(dāng)前Handler中的異常,而afterCompetition()方法則會(huì)保證所有步驟之后一定會(huì)進(jìn)行調(diào)用的,五路是否拋出異常;

  • 對(duì)于Handler的使用,我們希望能夠達(dá)到的目的是,使用方只需要實(shí)現(xiàn)該接口,并且使用某個(gè)注解來(lái)將其標(biāo)志位spring bean即可,無(wú)需管整個(gè)pipeline的組裝和流程控制。通過(guò)這種方式,我們既保留了每個(gè)spring提供給我們的便利性,也使用了pipeline模式的靈活性
    上述流程代碼中,我們注意到,每個(gè)層級(jí)的方法中都有一個(gè)HandlerContext用于傳遞鏈的相關(guān)控制信息,我們來(lái)看下關(guān)于這部分的源碼:

@Component
@Scope("prototype")
public class HandlerContext {

  HandlerContext prev;
  HandlerContext next;
  Handler handler;

  private Task task;

  public void fireTaskReceived(Request request) {
    invokeTaskReceived(next(), request);
  }

  /**
   * 處理接收到任務(wù)的事件
   */
  static void invokeTaskReceived(HandlerContext ctx, Request request) {
    if (ctx != null) {
      try {
        ctx.handler().receiveTask(ctx, request);
      } catch (Throwable e) {
        ctx.handler().exceptionCaught(ctx, e);
      }
    }
  }

  public void fireTaskFiltered(Task task) {
    invokeTaskFiltered(next(), task);
  }

  /**
   * 處理任務(wù)過(guò)濾事件
   */
  static void invokeTaskFiltered(HandlerContext ctx, Task task) {
    if (null != ctx) {
      try {
        ctx.handler().filterTask(ctx, task);
      } catch (Throwable e) {
        ctx.handler().exceptionCaught(ctx, e);
      }
    }
  }

  public void fireTaskExecuted(Task task) {
    invokeTaskExecuted(next(), task);
  }

  /**
   * 處理執(zhí)行任務(wù)事件
   */
  static void invokeTaskExecuted(HandlerContext ctx, Task task) {
    if (null != ctx) {
      try {
        ctx.handler().executeTask(ctx, task);
      } catch (Exception e) {
        ctx.handler().exceptionCaught(ctx, e);
      }
    }
  }

  public void fireAfterCompletion(HandlerContext ctx) {
    invokeAfterCompletion(next());
  }

  static void invokeAfterCompletion(HandlerContext ctx) {
    if (null != ctx) {
      ctx.handler().afterCompletion(ctx);
    }
  }

  private HandlerContext next() {
    return next;
  }

  private Handler handler() {
    return handler;
  }
}

在HandlerContext中,我們需要說(shuō)明幾點(diǎn):

  • 之前Handler接口默認(rèn)實(shí)現(xiàn)的ctx.filterXXX()方法,這里都委托給了對(duì)應(yīng)的invokeXXX方法進(jìn)行調(diào)用,而且我們注意到,在傳遞invokeXXX()方法的參數(shù)里,傳入的HandlerContext對(duì)象都是通過(guò)next()方法獲取到的。也就是說(shuō)我們?cè)贖andler中調(diào)用ctx.filterXXX方法時(shí),都是在調(diào)用當(dāng)前Handler的下一個(gè)Handler對(duì)應(yīng)的層級(jí)方法,通過(guò)這種方式我們就可以實(shí)現(xiàn)鏈?zhǔn)降膫鬟f調(diào)用;

  • 在上一點(diǎn)中我們說(shuō)到,在某個(gè)Handler中如果想讓鏈往下傳遞,只需要調(diào)用FilterXXX()方法即可,如果我們?cè)谀硞€(gè)Handler中,根據(jù)業(yè)務(wù),當(dāng)前層級(jí)已經(jīng)調(diào)用完成,而無(wú)需調(diào)用后續(xù)的Handler,那么我們就不需要調(diào)用ctx.filterXXX()方法即可;

  • 在HandlerContext中,我們也實(shí)現(xiàn)了invokeXXX()方法,該方法的作用是提供給外部的pipeline調(diào)用的,開(kāi)啟每個(gè)層級(jí)的鏈;

  • 在每個(gè)invokeXXX()方法中,我們都是用try...catch將當(dāng)前層級(jí)的調(diào)用拋出異常捕獲了,然后調(diào)用ctx.handler().exceptionCaught()方法即可,異常捕獲流程就是在這里的HandlerContext()中處理的;

  • 在HandlerContext的聲明處,我們需要注意到,其使用了@conpoment和@("prototype")注解進(jìn)行標(biāo)注了,這說(shuō)明我們的HandlerContext是有spring 容器管理的,并且由于我們每個(gè)Handler實(shí)際都由HandlerContext維護(hù),所以這里必須聲明為prototype類型。通過(guò)這種方式,我們的HandlerContext也就具備著諸如spring相關(guān)的bean的功能,能夠根據(jù)業(yè)務(wù)需求進(jìn)行一些額外的處理;
    前面我們講解了Handler和HandlerContext的具體實(shí)現(xiàn),以及實(shí)現(xiàn)的過(guò)程需要注意的一些問(wèn)題,下面我們將來(lái)看進(jìn)行流程控制的pipeline是如何實(shí)現(xiàn)的,如下是其接口的定義:

public interface Pipeline {
  
  Pipeline fireTaskReceived();
  
  Pipeline fireTaskFiltered();
  
  Pipeline fireTaskExecuted();
  
  Pipeline fireAfterCompletion();
}

這里主要是定義了一個(gè)pipeline接口,該接口定義了一些列的層級(jí)調(diào)用,是每個(gè)層級(jí)的入口方法,如下是該接口的實(shí)現(xiàn)類:

@Component("pipeline")
@Scope("prototype")
public class DefaultPipeline implements Pipeline, ApplicationContextAware, InitializingBean {
  // 創(chuàng)建一個(gè)默認(rèn)的handler,將其注入到首尾兩個(gè)節(jié)點(diǎn)的HandlerContext中,其作用只是將鏈往下傳遞
  private static final Handler DEFAULT_HANDLER = new Handler() {};

  // 將ApplicationContext注入進(jìn)來(lái)的主要原因在于,HandlerContext是prototype類型的,因而需要
  // 通過(guò)ApplicationContext.getBean()方法來(lái)獲取其實(shí)例
  private ApplicationContext context;

  // 創(chuàng)建一個(gè)頭結(jié)點(diǎn)和尾節(jié)點(diǎn),這兩個(gè)節(jié)點(diǎn)內(nèi)部沒(méi)有做任何處理,只是默認(rèn)的將每一層級(jí)的鏈往下傳遞,
  // 這里頭結(jié)點(diǎn)和尾節(jié)點(diǎn)的主要作用就是用于標(biāo)志整個(gè)鏈的首尾,所有的業(yè)務(wù)節(jié)點(diǎn)都在這兩個(gè)節(jié)點(diǎn)中間
  private HandlerContext head;
  private HandlerContext tail;

  // 用于業(yè)務(wù)調(diào)用的request對(duì)象,其內(nèi)部封裝了業(yè)務(wù)數(shù)據(jù)
  private Request request;
  // 用于執(zhí)行任務(wù)的task對(duì)象
  private Task task;

  // 最初始的業(yè)務(wù)數(shù)據(jù)需要通過(guò)構(gòu)造函數(shù)傳入,因?yàn)檫@是驅(qū)動(dòng)整個(gè)pipeline所需要的數(shù)據(jù),
  // 一般通過(guò)外部調(diào)用方的參數(shù)進(jìn)行封裝即可
  public DefaultPipeline(Request request) {
    this.request = request;
  }

  // 這里我們可以看到,每一層級(jí)的調(diào)用都是通過(guò)HandlerContext.invokeXXX(head)的方式進(jìn)行的,
  // 也就是說(shuō)我們每一層級(jí)鏈的入口都是從頭結(jié)點(diǎn)開(kāi)始的,當(dāng)然在某些情況下,我們也需要從尾節(jié)點(diǎn)開(kāi)始鏈
  // 的調(diào)用,這個(gè)時(shí)候傳入tail即可。
  @Override
  public Pipeline fireTaskReceived() {
    HandlerContext.invokeTaskReceived(head, request);
    return this;
  }

  // 觸發(fā)任務(wù)過(guò)濾的鏈調(diào)用
  @Override
  public Pipeline fireTaskFiltered() {
    HandlerContext.invokeTaskFiltered(head, task);
    return this;
  }

  // 觸發(fā)任務(wù)執(zhí)行的鏈執(zhí)行
  @Override
  public Pipeline fireTaskExecuted() {
    HandlerContext.invokeTaskExecuted(head, task);
    return this;
  }

  // 觸發(fā)最終完成的鏈的執(zhí)行
  @Override
  public Pipeline fireAfterCompletion() {
    HandlerContext.invokeAfterCompletion(head);
    return this;
  }
  
  // 用于往Pipeline中添加節(jié)點(diǎn)的方法,讀者朋友也可以實(shí)現(xiàn)其他的方法用于進(jìn)行鏈的維護(hù)
  void addLast(Handler handler) {
    HandlerContext handlerContext = newContext(handler);
    tail.prev.next = handlerContext;
    handlerContext.prev = tail.prev;
    handlerContext.next = tail;
    tail.prev = handlerContext;
  }

  // 這里通過(guò)實(shí)現(xiàn)InitializingBean接口來(lái)達(dá)到初始化Pipeline的目的,可以看到,這里初始的時(shí)候
  // 我們通過(guò)ApplicationContext實(shí)例化了兩個(gè)HandlerContext對(duì)象,然后將head.next指向tail節(jié)點(diǎn),
  // 將tail.prev指向head節(jié)點(diǎn)。也就是說(shuō),初始時(shí),整個(gè)鏈只有頭結(jié)點(diǎn)和尾節(jié)點(diǎn)。
  @Override
  public void afterPropertiesSet() throws Exception {
    head = newContext(DEFAULT_HANDLER);
    tail = newContext(DEFAULT_HANDLER);
    head.next = tail;
    tail.prev = head;
  }

  // 使用默認(rèn)的Handler初始化一個(gè)HandlerContext
  private HandlerContext newContext(Handler handler) {
    HandlerContext context = this.context.getBean(HandlerContext.class);
    context.handler = handler;
    return context;
  }

  // 注入ApplicationContext對(duì)象
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) {
    this.context = applicationContext;
  }
}

關(guān)于defaultPipeline的實(shí)現(xiàn),有以下幾點(diǎn)需要說(shuō)明:

  • defaultpipeline 使用@compoment和@scope("prototype")注解進(jìn)行了標(biāo)注,當(dāng)前一個(gè)注解用于將其聲明為一個(gè)spring容器所管理的bean,而后一個(gè)注解則用于表征defaultPipeline是個(gè)多例類型,很明顯的,這里的pipeLine是有狀態(tài)的。這里需要進(jìn)行說(shuō)明的是,'有狀態(tài)'主要是因?yàn)槲覀兛赡軙?huì)根據(jù)業(yè)務(wù)情況的動(dòng)態(tài)的調(diào)整整個(gè)鏈的節(jié)點(diǎn)情況,而且這里的request和task對(duì)象都是與具體的業(yè)務(wù)相關(guān)的,因?yàn)楸仨毬暶鳛閜rototype類型;

  • 上面的示例中,request對(duì)象是通過(guò)構(gòu)造pipeline對(duì)象的時(shí)候傳進(jìn)來(lái)的,而task對(duì)象則是在pipeline的流轉(zhuǎn)過(guò)程中生成的,這里比如通過(guò)完成filterTaskReceived()鏈的調(diào)用之后,就需要通過(guò)外部請(qǐng)求request得到一個(gè)task對(duì)象,從而進(jìn)行后續(xù)的處理;

  • 對(duì)于后續(xù)寫(xiě)業(yè)務(wù)代碼的人而言,其只需要實(shí)現(xiàn)一個(gè)Handler接口即可,無(wú)需要處理鏈相關(guān)的所有邏輯,以為我們需要獲取到所有實(shí)現(xiàn)Handler接口的bean;

  • 將實(shí)現(xiàn)了Handler接口的bean通過(guò)Handlercontext進(jìn)行封裝,然后將其添加到pipeline中
    這里的第一個(gè)問(wèn)題好處理,因?yàn)橥ㄟ^(guò)ApplicationContext就可以獲取實(shí)現(xiàn)了某個(gè)接口的所有bean,而第二個(gè)問(wèn)題我們可以通過(guò)聲明了BeanPostProcessoor接口的類來(lái)實(shí)現(xiàn)。如下是具體的實(shí)現(xiàn)代碼:

verride
  public void setApplicationContext(ApplicationContext applicationContext) {
    this.context = applicationContext;
  }
}
       關(guān)于DefaultPipeline的實(shí)現(xiàn),主要有如下幾點(diǎn)需要說(shuō)明:

DefaultPipeline使用@Component和@Scope("prototype")注解進(jìn)行了標(biāo)注,前一個(gè)注解用于將其聲明為一個(gè)Spring容器所管理的bean,而后一個(gè)注解則用于表征DefaultPipeline是一個(gè)多例類型的,很明顯,這里的Pipeline是有狀態(tài)的。這里需要進(jìn)行說(shuō)明的是,"有狀態(tài)"主要是因?yàn)槲覀兛赡軙?huì)根據(jù)業(yè)務(wù)情況動(dòng)態(tài)的調(diào)整個(gè)鏈的節(jié)點(diǎn)情況,而且這里的Request和Task對(duì)象都是與具體的業(yè)務(wù)相關(guān)的,因而必須聲明為prototype類型;
上面的示例中,Request對(duì)象是通過(guò)構(gòu)造Pipeline對(duì)象的時(shí)候傳進(jìn)來(lái)的,而Task對(duì)象則是在Pipeline的流轉(zhuǎn)過(guò)程中生成的,這里比如通過(guò)完成fireTaskReceived()鏈的調(diào)用之后,就需要通過(guò)外部請(qǐng)求Request得到一個(gè)Task對(duì)象,從而進(jìn)行整個(gè)Pipeline的后續(xù)處理;
       這里我們已經(jīng)實(shí)現(xiàn)了Pipeline,HandlerContext和Handler,知道這些bean都是被Spring所管理的bean,那么我們接下來(lái)的問(wèn)題主要在于如何進(jìn)行整個(gè)鏈的組裝。這里的組裝方式比較簡(jiǎn)單,其主要需要解決兩個(gè)問(wèn)題:

對(duì)于后續(xù)寫(xiě)業(yè)務(wù)代碼的人而言,其只需要實(shí)現(xiàn)一個(gè)Handler接口即可,而無(wú)需處理與鏈相關(guān)的所有邏輯,因而我們需要獲取到所有實(shí)現(xiàn)了Handler接口的bean;
將實(shí)現(xiàn)了Handler接口的bean通過(guò)HandlerContext進(jìn)行封裝,然后將其添加到Pipeline中。
       這里的第一個(gè)問(wèn)題比較好處理,因?yàn)橥ㄟ^(guò)ApplicationContext就可以獲取實(shí)現(xiàn)了某個(gè)接口的所有bean,而第二個(gè)問(wèn)題我們可以通過(guò)聲明一個(gè)實(shí)現(xiàn)了BeanPostProcessor接口的類來(lái)實(shí)現(xiàn)。如下是其實(shí)現(xiàn)代碼:

@Component
public class HandlerBeanProcessor implements BeanPostProcessor, ApplicationContextAware {

  private ApplicationContext context;

  // 該方法會(huì)在一個(gè)bean初始化完成后調(diào)用,這里主要是在Pipeline初始化完成之后獲取所有實(shí)現(xiàn)了
  // Handler接口的bean,然后通過(guò)調(diào)用Pipeline.addLast()方法將其添加到pipeline中
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof DefaultPipeline) {
      DefaultPipeline pipeline = (DefaultPipeline) bean;
      Map<String, Handler> handlerMap = context.getBeansOfType(Handler.class);
      handlerMap.forEach((name, handler) -> pipeline.addLast(handler));
    }

    return bean;
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) {
    this.context = applicationContext;
  }
}

這里我們整個(gè)鏈路的維護(hù)工作就完成,可以看到,現(xiàn)在基本已經(jīng)實(shí)現(xiàn)了鏈?zhǔn)搅鞒炭刂?。這里需要說(shuō)明的一點(diǎn)是,上面的HandlerBeanProcessor.postProcessAfterInitialization()方法的執(zhí)行是在InitializingBean.afterPropertySet()方法之后執(zhí)行的,也就是說(shuō)這里的HandlerBeanProcessor在執(zhí)行的時(shí)候,整個(gè)pipeline就是已經(jīng)完成初始化的了。下面我們來(lái)看下外部客戶端如何進(jìn)行這個(gè)鏈路流程的控制:

HandlerBeanProcessor在執(zhí)行時(shí),整個(gè)Pipeline是已經(jīng)初始化完成了的。下面我們來(lái)看一下外部客戶端如何進(jìn)行整個(gè)鏈?zhǔn)橇鞒痰目刂疲?

@Service
public class ApplicationService {

  @Autowired
  private ApplicationContext context;
  
  public void mockedClient() {
    Request request = new Request();  // request一般是通過(guò)外部調(diào)用獲取
    Pipeline pipeline = newPipeline(request);
    try {
      pipeline.fireTaskReceived();
      pipeline.fireTaskFiltered();
      pipeline.fireTaskExecuted();
    } finally {
      pipeline.fireAfterCompletion();
    }
  }
  
  private Pipeline newPipeline(Request request) {
    return context.getBean(DefaultPipeline.class, request);
  }
}

這里我們模擬一個(gè)客戶端的調(diào)用,首先創(chuàng)建了一個(gè)pipeline對(duì)象,然后依次調(diào)用各個(gè)層級(jí)的方法,并且這里我們使用try....finally結(jié)構(gòu)來(lái)保證Pipeline.fireAfterCompletion()方法一定會(huì)執(zhí)行。如此我們就完成了整個(gè)責(zé)任鏈路模式的構(gòu)造。這里我們使用前面用到的時(shí)效性的filter來(lái)作為示例來(lái)實(shí)現(xiàn)一個(gè)Handler:

@Component
public class DurationHandler implements Handler {

  @Override
  public void filterTask(HandlerContext ctx, Task task) {
    System.out.println("時(shí)效性檢驗(yàn)");
    ctx.fireTaskFiltered(task);
  }
}

關(guān)于這里具體的業(yè)務(wù)我們需要說(shuō)明的有如下幾點(diǎn):

  • 改Handler必須使用@compoment注解來(lái)將其聲明為spring容器管理的bean,這樣我們前面實(shí)現(xiàn)的HandlerBeanProcessor 才能動(dòng)態(tài)的添加到整個(gè)pipeline中;

  • 在每個(gè)Handler中,需要根據(jù)當(dāng)前的業(yè)務(wù)需要來(lái)實(shí)現(xiàn)具體的層級(jí)方法,比如這里是進(jìn)行時(shí)效性檢驗(yàn)的,就是"任務(wù)過(guò)濾"這一層級(jí)的邏輯,因?yàn)闀r(shí)效性檢驗(yàn)通過(guò)我們才能執(zhí)行這個(gè)task,因而這里需要實(shí)現(xiàn)的是Handler.filterTask()方法,如果我們需要實(shí)現(xiàn)的是執(zhí)行task的邏輯,那么需要實(shí)現(xiàn)的是Handler.executeTask()方法; 在實(shí)現(xiàn)完具體的業(yè)務(wù)邏輯之后,我們可以根據(jù)當(dāng)前的業(yè)務(wù)需要看是否需要將當(dāng)前層級(jí)的鏈繼續(xù)往下傳遞,也就是這里的ctx.fireTaskFiltered(task);方法的調(diào)用,我們可以看前面HandlerContext.fireXXX()方法就是會(huì)獲取當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),然后進(jìn)行調(diào)用。如果根據(jù)業(yè)務(wù)需要,不需要將鏈往下傳遞,那么就不需要調(diào)用ctx.fireTaskFiltered(task);

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。

文章標(biāo)題:spring中怎么使用責(zé)任連模式
當(dāng)前鏈接:http://bm7419.com/article34/pcsise.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、網(wǎng)站排名、定制網(wǎng)站、企業(yè)網(wǎng)站制作、網(wǎng)站改版、網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司