使用命令總線的優(yōu)點之一,是能夠基于所有傳入的命令采取相應的行動。例子是,不論哪種命令類型,你都希望去做日志記錄或身份驗證。這是使用攔截器來完成的。
有不同類型的攔截器:Dispatch攔截器和Handler攔截器。前者在命令被分發(fā)到命令處理程序之前被調(diào)用。在那時,它甚至不能確定該命令有任何處理器的存在。后者在命令處理程序被調(diào)用之前調(diào)用。
Message Dispatch Interceptors(消息分發(fā)攔截器)
當命令在命令總線上被分發(fā)時調(diào)用消息分發(fā)攔截器。例如,它們可以通過添加元數(shù)據(jù)來更改命令消息,或通過拋出異常來阻塞命令。這些攔截器總是在分發(fā)命令的線程上被調(diào)用。
Structural validation(結構性驗證)
如果它沒有包含正確格式的所有必需的信息,那么處理命令是沒有意義的,。事實上,一個命令缺乏信息應該盡可能早地被阻塞,甚至最好是任何事務開始之前。因此,攔截器應該檢查所有傳入命令的信息的可用性。這就是所謂的結構性驗證。
Axon Framework支持基于JSR 303 Bean Validation的驗證。這允許你用像@NotEmpty和@Pattern這樣的注解,去注解命令上的字段。你需要在你的類路徑中include一個JSR 303實現(xiàn)(比如Hibernate-Validator)。然后,在命令總線上配置一個BeanValidationInterceptor,它會自動找到并配置你的驗證器實現(xiàn)。雖然它使用合理的默認值,但你可以根據(jù)具體需要調(diào)整它。
提示
你想在一個無效的命令上使用盡可能少的資源。因此,該攔截器通常是位于攔截器鏈的最前端。在某些情況下,一個日志或審計攔截器可能需要放在前面,緊跟著它的是驗證攔截器。
BeanValidationInterceptor還實現(xiàn)了MessageHandlerInterceptor,也允許你把它配置為一個處理程序(Handler)攔截器。
Message Handler Interceptors(消息處理程序攔截器)
消息處理程序攔截器可以在命令處理之前和之后執(zhí)行操作。攔截器甚至可以完全阻止命令處理,例如出于安全原因。
攔截器必須實現(xiàn)MessageHandlerInterceptor接口。該接口聲明了一個方法handle,它需要三個參數(shù):命令消息,當前的UnitOfWork和InterceptorChain。InterceptorChain用于繼續(xù)分發(fā)處理。
與分發(fā)攔截器不同,處理程序攔截器在命令處理程序上下文中被調(diào)用。這意味著它們可以根據(jù)正在處理消息的工作單元附上相關數(shù)據(jù)。然后這個相關數(shù)據(jù)將被附加到在工作單元的上下文中被創(chuàng)建的消息。
處理程序攔截器也通常用于管理圍繞命令處理的事務。這么做,注冊一個TransactionManagingInterceptor,使用TransactionManager依次配置啟動和提交(或回滾)實際事務。
Distributing the Command Bus(分布式命令總線)
CommandBus的實現(xiàn)在早期聲稱只允許命令消息在單個JVM上分發(fā)。有時候,你想把不同JVM中的命令總線的多個實例作為一個。當返回任何結果時,在一個JVM命令總線上發(fā)出的命令應該無縫地傳到到另一個JVM中的命令處理程序。
這就是DistributedCommandBus(分布式命令總線)的由來。不像其他CommandBus的實現(xiàn),DistributedCommandBus不調(diào)用任何處理器。它的作用是在不同的JVM的命令總線實現(xiàn)之間形成一座“橋”。每個JVM上的DistributedCommandBus實例稱為“Segment”。

請注意
雖然分布式命令總線本身是Axon Framework核心模塊的一部分,但它需要的組件,你可以在其中一個以axon-distributed-commandbus -開頭的模塊中找到。如果你使用Maven,確保你有適當?shù)囊蕾嚰?。groupId和version與核心模塊相同。
DistributedCommandBus依賴于兩個組件:一個是CommandBusConnector,實現(xiàn)JVM的之間的通信協(xié)議;一個是CommandRouter,為每個傳入的命令選擇目的地。這個路由器定義分布式總線命令的segment應該given一個命令,根據(jù)路由鍵計算得到路由策略。兩個具有相同路由鍵的命令將始終路由到相同的segment,只要segment數(shù)量和配置沒有改變。一般來說,用目標聚合的標識符作為路由鍵。
提供兩個RoutingStrategy的實現(xiàn):MetaDataRoutingStrategy,它使用元數(shù)據(jù)屬性在命令消息中查找路由鍵,而AnnotationRoutingStrategy,它使用注解在命令消息有效負載上的@TargetAggregateIdentifier來提取路由鍵。顯然,你也可以提供自己的實現(xiàn)。
默認情況下,當命令消息沒有鍵能被解析時,RoutingStrategy實現(xiàn)將拋出一個異常時。這種行為可以改變,通過在MetaDataRoutingStrategy或AnnotationRoutingStrategy的構造函數(shù)中提供一個UnresolvedRoutingKeyPolicy。有三個可能的策略:
- ERROR:這是默認值,當路由鍵不可用時,會拋出一個異常。
- RANDOM_KEY:將返回一個隨機值,當一個路由鍵不能從命令消息解析。這實際上意味著這些命令將被路由到命令總線的隨機segment。
- STATIC_KEY:將返回一個靜態(tài)鍵(現(xiàn)有的“未被解析的”)為未被解析的路由鍵。這實際上意味著所有這些命令將被路由到相同的segment,只要segment的配置不改變。