從Servlet、Dubbo、Mybatis聊聊責(zé)任鏈究竟怎么用
作者:atheva?
來源:https://dwz.cn/1TtgW7Ud
責(zé)任鏈模式的定義:使多個對象都有機(jī)會處理請求,從而避免請求的發(fā)送者和接受者之間的耦合關(guān)系, 將這個對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理他為止。這里就不再過多的介紹什么是責(zé)任鏈模式,主要來說說java中如何編寫。主要從下面3個框架中的代碼中介紹。
servlet中的filter
dubbo中的filter
mybatis中的plugin
servlet中的Filter
servlet中分別定義了一個 Filter和FilterChain的接口,核心代碼如下:
public?final?class?ApplicationFilterChain?implements?FilterChain?{
????private?int?pos?=?0;?//當(dāng)前執(zhí)行filter的offset
????private?int?n;?//當(dāng)前filter的數(shù)量
????private?ApplicationFilterConfig[]?filters;??//filter配置類,通過getFilter()方法獲取Filter
????private?Servlet?servlet
????@Override
????public?void?doFilter(ServletRequest?request,?ServletResponse?response)?{
????????if?(pos?????????????ApplicationFilterConfig?filterConfig?=?filters[pos++];
????????????Filter?filter?=?filterConfig.getFilter();
????????????filter.doFilter(request,?response,?this);
????????}?else?{
????????????//?filter都處理完畢后,執(zhí)行servlet
????????????servlet.service(request,?response);
????????}
????}
}
代碼還算簡單,結(jié)構(gòu)也比較清晰,定義一個Chain,里面包含了Filter列表和servlet,達(dá)到在調(diào)用真正servlet之前進(jìn)行各種filter邏輯。

Dubbo中的Filter
Dubbo在創(chuàng)建Filter的時候是另外一個方法,通過把Filter封裝成 Invoker的匿名類,通過鏈表這樣的數(shù)據(jù)結(jié)構(gòu)來完成責(zé)任鏈,核心代碼如下:
private?static??Invoker?buildInvokerChain(final?Invoker?invoker,?String?key,?String?group) ? {
????Invoker?last?=?invoker;
????//只獲取滿足條件的Filter
????List?filters?=?ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(),?key,?group);
????if?(filters.size()?>?0)?{
????????for?(int?i?=?filters.size()?-?1;?i?>=?0;?i?--)?{
????????????final?Filter?filter?=?filters.get(i);
????????????final?Invoker?next?=?last;
????????????last?=?new?Invoker()?{
????????????????...
????????????????public?Result?invoke(Invocation?invocation)?throws?RpcException?{
????????????????????return?filter.invoke(next,?invocation);
????????????????}
????????????????...
????????????};
????????}
????}
????return?last;
}
Dubbo的責(zé)任鏈就沒有類似FilterChain這樣的類吧Filter和調(diào)用Invoker結(jié)合起來,而是通過創(chuàng)建一個鏈表,調(diào)用的時候我們只知道第一個節(jié)點(diǎn),每個節(jié)點(diǎn)包含了下一個調(diào)用的節(jié)點(diǎn)信息。 這里的雖然Invoker封裝Filter沒有顯示的指定next,但是通過java匿名類和final的機(jī)制達(dá)到同樣的效果。

Mybatis中的Plugin
Mybatis可以配置各種Plugin,無論是官方提供的還是自己定義的,Plugin和Filter類似,就在執(zhí)行Sql語句的時候做一些操作。Mybatis的責(zé)任鏈則是通過動態(tài)代理的方式,使用Plugin代理實(shí)際的Executor類。(這里實(shí)際還使用了組合模式,因為Plugin可以嵌套代理),核心代碼如下:
public?class?Plugin?implements?InvocationHandler{
????private?Object?target;
????private?Interceptor?interceptor;
????@Override
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{??????
????????if?(滿足代理條件)?{
????????????return?interceptor.intercept(new?Invocation(target,?method,?args));
????????}
????????return?method.invoke(target,?args);?????
????}
????//對傳入的對象進(jìn)行代理,可能是實(shí)際的Executor類,也可能是Plugin代理類
????public?static?Object?wrap(Object?target,?Interceptor?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;
????}
}?
簡單的示意圖如下:

總結(jié)
這里簡單介紹了Servlet、Dubbo、Mybatis對責(zé)任鏈模式的不同實(shí)現(xiàn)手段,其中Servlet是相對比較清晰,又易于實(shí)現(xiàn)的方式,而Dubbo和Mybatis則適合在原有代碼基礎(chǔ)上,增加責(zé)任鏈模式代碼改動量最小的。
