責任鏈用在那些場景?
我們需要定義一篇處理請求的責任處理器,避免請求過來的處理請求的接收者過於耦合在一個處理邏輯中,將這個對象連成一條鏈,請求將沿着預先定義好的鏈進行傳遞,知道找到一個可以處理的對象責任處理器。
具體有以下幾個關鍵點:
- 初始化責任鏈管理容器。
- 請求在鏈上尋找責任處理器。
我們實際接觸的有哪些?
web服務中的過濾器鏈
//責任鏈管理容器
final class ApplicationFilterChain implements FilterChain, CometFilterChain {
private static final ThreadLocal<ServletRequest> lastServicedRequest;
private static final ThreadLocal<ServletResponse> lastServicedResponse;
static {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest = new ThreadLocal<>();
lastServicedResponse = new ThreadLocal<>();
} else {
lastServicedRequest = null;
lastServicedResponse = null;
}
}
public static final int INCREMENT = 10;
/**
* 構造一個新的責任鏈實例容器.
*/
public ApplicationFilterChain() {
super();
}
/**
* Filters.
*/
private ApplicationFilterConfig[] filters =
new ApplicationFilterConfig[0];
/**
* 當前責任鏈Filer的下標
*/
private int pos = 0;
/**
* 當前過濾列表中過濾器的數量
*/
private int n = 0;
/**
* 被鏈執行的servlet
*/
private Servlet servlet = null;
/**
* The string manager for our package.
*/
private static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* 實例包裝增強
*/
private InstanceSupport support = null;
private static final Class<?>[] classType = new Class[]{
ServletRequest.class, ServletResponse.class, FilterChain.class};
private static final Class<?>[] classTypeUsedInService = new Class[]{
ServletRequest.class, ServletResponse.class};
private static final Class<?>[] cometClassType =
new Class[]{ CometEvent.class, CometFilterChain.class};
private static final Class<?>[] classTypeUsedInEvent =
new Class[] { CometEvent.class };
/**
* 請求和響應過濾實現該過濾方法
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);
}
}
//這裏是操作
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 如果有下一個過濾器,進行下一個
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
filter = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, request, response);
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", filter, classType, args, principal);
} else {
//主意這裏
filter.doFilter(request, response, this);
}
//下邊的servlet的處理。這裏不見紹 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
} catch (IOException | ServletException | RuntimeException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw new ServletException
(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
servlet, request, response);
if (request.isAsyncSupported()
&& !support.getWrapper().isAsyncSupported()) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} else {
servlet.service(request, response);
}
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);
} catch (IOException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (ServletException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (RuntimeException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw new ServletException
(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
/**
* Process the event, using the security manager if the option is enabled.
*
* @param event the event to process
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
@Override
public void doFilterEvent(CometEvent event)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final CometEvent ev = event;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilterEvent(ev);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilterEvent(event);
}
}
/**
* The last request passed to a servlet for servicing from the current
* thread.
*
* @return The last request to be serviced.
*/
public static ServletRequest getLastServicedRequest() {
return lastServicedRequest.get();
}
/**
* The last response passed to a servlet for servicing from the current
* thread.
*
* @return The last response to be serviced.
*/
public static ServletResponse getLastServicedResponse() {
return lastServicedResponse.get();
}
private void internalDoFilterEvent(CometEvent event)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
CometFilter filter = null;
try {
filter = (CometFilter) filterConfig.getFilter();
// FIXME: No instance listener processing for events for now
/*
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, event);
*/
if( Globals.IS_SECURITY_ENABLED ) {
final CometEvent ev = event;
Principal principal =
ev.getHttpServletRequest().getUserPrincipal();
Object[] args = new Object[]{ev, this};
SecurityUtil.doAsPrivilege("doFilterEvent", filter,
cometClassType, args, principal);
} else {
filter.doFilterEvent(event, this);
}
/*support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, event);*/
} catch (IOException e) {
/*
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, event, e);
*/
throw e;
} catch (ServletException e) {
/*
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, event, e);
*/
throw e;
} catch (RuntimeException e) {
/*
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, event, e);
*/
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
/*if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, event, e);*/
throw new ServletException
(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
/*
support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
servlet, request, response);
*/
if( Globals.IS_SECURITY_ENABLED ) {
final CometEvent ev = event;
Principal principal =
ev.getHttpServletRequest().getUserPrincipal();
Object[] args = new Object[]{ ev };
SecurityUtil.doAsPrivilege("event",
servlet,
classTypeUsedInEvent,
args,
principal);
} else {
((CometProcessor) servlet).event(event);
}
/*
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);*/
} catch (IOException e) {
/*
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
*/
throw e;
} catch (ServletException e) {
/*
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
*/
throw e;
} catch (RuntimeException e) {
/*
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
*/
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
/*
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
*/
throw new ServletException
(sm.getString("filterChain.servlet"), e);
}
}
/**
* Add a filter to the set of filters that will be executed in this chain.
*
* @param filterConfig The FilterConfig for the servlet to be executed
*/
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
/**
* Release references to the filters and wrapper executed by this chain.
*/
void release() {
for (int i = 0; i < n; i++) {
filters[i] = null;
}
n = 0;
pos = 0;
servlet = null;
support = null;
}
/**
* Prepare for reuse of the filters and wrapper executed by this chain.
*/
void reuse() {
pos = 0;
}
/**
* Set the servlet that will be executed at the end of this chain.
*
* @param servlet The Wrapper for the servlet to be executed
*/
void setServlet(Servlet servlet) {
this.servlet = servlet;
}
/**
* Set the InstanceSupport object used for event notifications
* for this filter chain.
*
* @param support The InstanceSupport object for our Wrapper
*/
void setSupport(InstanceSupport support) {
this.support = support;
}
}
就是定義一個過濾鏈容器,裏邊包涵Filter列表和servlet,在調用servlet之前先執行一系列的Filter
request -> … Filters… -> servlet
dubbo中的過濾鏈
Dubbo在創建Filter的時候使用的方法也差不多,只是將Filer封裝成代理調用對象。過Dubbo內置的Filter或用戶自定義的Filter來創建調用鏈完成。當發起方法調用時,會執行調用鏈各個結點的方法,以完成一些處理工作。
public interface Filter {
Result invoke(Invoker<?> var1, Invocation var2) throws RpcException;
}
//1.在構建調用鏈時方法先獲取Filter列表
//2.創建與Fitler數量一樣多Invoker結點
//3.將節點串聯構成一個鏈表
//4.Invoker結點的串聯,最後將這個鏈的首結點返回
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
final Invoker<T> last = invoker;
//類似於類加載器加載Filter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
//逆向遞歸創建,最後指針指向第一個節點
//可以看到鏈表的最後一個結點就是buildInvokerChain 方法的入參invoker。最終buildInvokerChain方法通過鏈表頭插法完成調用鏈的創建。因此在真正的調用請求處理前會經過若干filter進行預處理。
//這裏調用鏈的創建可以看作是職責鏈模式(Chain of Responsibility Pattern)的一個實現。這樣系統中增加一個新的過濾器預處理請求時,無須修改原有系統的代碼,只需重新建調用鏈即可。
for(int i = filters.size() - 1; i >= 0; --i) {
final Filter filter = (Filter)filters.get(i);
last = new Invoker<T>() {
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}
public boolean isAvailable() {
return invoker.isAvailable();
}
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(last, invocation);
}
public void destroy() {
invoker.destroy();
}
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
這個比較高大上,用到遞歸的操作,這裏半天沒有明白,寫個簡單的例子理解一下
public interface Filter {
int invoke(Invoker invoker);
}
class Filter1 implements Filter{
public int invoke(Invoker invoker) {
System.out.println("Filter1 invoker");
return invoker.invoke();
}
}
class Filter2 implements Filter{
public int invoke(Invoker invoker) {
System.out.println("Filter2 invoker");
return invoker.invoke();
}
}
public interface Invoker {
int invoke();
}
public class Test {
public static void main(String[] args) {
List<Filter> filters = Arrays.asList(new Filter1(),new Filter2());
Invoker last = () -> {
System.out.println("invoker");
return 0;
};
for(int i = filters.size() - 1; i >= 0; i--) {
// 獲取filter
final Filter filter = filters.get(i);
final Invoker next = last;
// last -> filter2中增加初始invoker的聲明 -> filter1中增加invoker2的調用聲明 -> 生成last的invoker的聲明
last = () -> filter.invoke(next);
}
//調用開始 ->
last.invoke();
}
}
//執行後的結果
Filter1 invoker
Filter2 invoker
invoker
mybatis中的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);
}
//對傳入的對象進行代理,可能是實際的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;
}
}
Mybatis可以配置各種Plugin,無論是官方提供的還是自己定義的,Plugin和Filter類似,就在執行Sql語句的時候做一些操作。Mybatis的責任鏈則是通過動態代理的方式,使用Plugin代理實際的Executor類。(這裏實際還使用了組合模式,因爲Plugin可以嵌套代理)