責任鏈模式
責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系, 將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理他為止。這里就不再過多的介紹什么是責任鏈模式,主要來說說java中如何編寫。主要從下面3個框架中的代碼中介紹。
- servlet中的filter
- dubbo中的filter
- mybatis中的plugin 這3個框架在實現責任鏈方式不盡相同。
servlet中的Filter
servlet中分別定義了一個 Filter和FilterChain的接口,核心代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
final
class
ApplicationFilterChain
implements
FilterChain {
private
int
pos =
0
;
//當前執行filter的offset
private
int
n;
//當前filter的數量
private
ApplicationFilterConfig[] filters;
//filter配置類,通過getFilter()方法獲取Filter
private
Servlet servlet
@Override
public
void
doFilter(ServletRequest request, ServletResponse response) {
if
(pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response,
this
);
}
else
{
// filter都處理完畢后,執行servlet
servlet.service(request, response);
}
}
}
|
代碼還算簡單,結構也比較清晰,定義一個Chain,里面包含了Filter列表和servlet,達到在調用真正servlet之前進行各種filter邏輯。
Dubbo中的Filter
Dubbo在創建Filter的時候是另外一個方法,通過把Filter封裝成 Invoker的匿名類,通過鏈表這樣的數據結構來完成責任鏈,核心代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private
static
<T> Invoker<T> buildInvokerChain(
final
Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
//只獲取滿足條件的Filter
List<Filter> 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<T> next = last;
last =
new
Invoker<T>() {
...
public
Result invoke(Invocation invocation)
throws
RpcException {
return
filter.invoke(next, invocation);
}
...
};
}
}
return
last;
}
|
Dubbo的責任鏈就沒有類似FilterChain這樣的類吧Filter和調用Invoker結合起來,而是通過創建一個鏈表,調用的時候我們只知道第一個節點,每個節點包含了下一個調用的節點信息。 這里的雖然Invoker封裝Filter沒有顯示的指定next,但是通過java匿名類和final的機制達到同樣的效果。
Mybatis中的Plugin
Mybatis可以配置各種Plugin,無論是官方提供的還是自己定義的,Plugin和Filter類似,就在執行Sql語句的時候做一些操作。Mybatis的責任鏈則是通過動態代理的方式,使用Plugin代理實際的Executor類。(這里實際還使用了組合模式,因為Plugin可以嵌套代理),核心代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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);
}
//對傳入的對象進行代理,可能是實際的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;
}
}
|
簡單的示意圖如下:
總結
這里簡單介紹了Servlet、Dubbo、Mybatis對責任鏈模式的不同實現手段,其中Servlet是相對比較清晰,又易於實現的方式,而Dubbo和Mybatis則適合在原有代碼基礎上,增加責任鏈模式代碼改動量最小的。
上面是轉發別人的。
下面是個人的模擬實現
1.web filter中的過濾器很多人都了解,所以不做模擬
2.dubbo中filter
public void test() { List<Filter> lists = new ArrayList<>(); lists.add(new SayWorldFilter()); lists.add(new SayHelloFilter()); Request request = new MyRequest(); for (Filter filter : lists) { Request next = request; request = new Request() { @Override public String test() { return filter.filter(next); } }; } request.test(); }
3.mybatis 中使用動態代理生產的包裝類實現過濾
Request request = (Request) (RequestWrap.wrap(new MyRequest(), new SayWorldFilter())); request = (Request) (RequestWrap.wrap(request, new SayHelloFilter())); request.test();
具體代碼見附件。