tomcat責任鏈設計模式 FilterChain原理解析
轉自:http://javapolo.iteye.com/blog/1287747
今天晚上花了些時間debug了下tomcat,注意觀察了下tomcat內部過濾器的實現,其實tomcat內部過濾器採用了責任鏈的設計模式,
(其實struts2攔截器那一塊採用了相似的設計模式),以下是個人對原始碼的解讀,
ApplicationFilterChain詳解
首先是對該類的定義的介紹
/** * Implementation of <code>javax.servlet.FilterChain</code> used to manage * the execution of a set of filters for a particular request. When the * set of defined filters has all been executed, the next call to * <code>doFilter()</code> will execute the servlet's <code>service()</code> * method itself. * * @author Craig R. McClanahan * @version $Id: ApplicationFilterChain.java 1078022 2011-03-04 15:52:01Z markt $ */ final class ApplicationFilterChain implements FilterChain, CometFilterChain
第一個疑問是該過濾器鏈裡面的過濾器源於哪裡?
答案是該類裡面包含了一個ApplicationFilterConfig物件,而該物件則是個filter容器
/**
* Filters.
*/
private ApplicationFilterConfig[] filters =
new ApplicationFilterConfig[0];
以下是ApplicationFilterConfig類的宣告
org.apache.catalina.core.ApplicationFilterConfig
Implementation of a javax.servlet.FilterConfig useful in managing the filter instances instantiated when a web application is first started.
當web容器啟動是ApplicationFilterConfig自動例項化,它會從該web工程的web.xml檔案中讀取配置的filter資訊,然後裝進該容器
下個疑問是它如何執行該過濾器容器裡面的filter呢?
答案是通過pos它來標識當前ApplicationFilterChain(當前過濾器鏈)執行到哪個過濾器
/**
* The int which is used to maintain the current position
* in the filter chain.
*/
private int pos = 0;
通過n來記錄當前過濾器鏈裡面擁有的過濾器數目
/** * The int which gives the current number of filters in the chain. */ private int n = 0;
通過addFilter方法向容器中新增一個filter(引數即為容器初始化生成的filterConfig物件)
/**
* 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)
ApplicationFilterChain採用責任鏈設計模式達到對不同過濾器的執行
首先ApplicationFilterChain 會呼叫它重寫FilterChain的doFilter方法,然後doFilter裡面會呼叫
internalDoFilter(request,response)方法;該方法使過濾器容器拿到每個過濾器,然後呼叫它們重寫Filter接口裡面的dofilter方法
以下是ApplicationFilterChain 裡面重寫FilterChain裡面的doFilter方法的描述
/**
* Invoke the next filter in this chain, passing the specified request
* and response. If there are no more filters in this chain, invoke
* the <code>service()</code> method of the servlet itself.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response)
以下是internalDoFilter的部分程式碼
// Call the next filter if there is one
if (pos < n) {
//先拿到下個過濾器,將指標向下移動一位
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
try {
//獲取當前指向的filter例項
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)方法
//ApplicationFilterChain裡面的filter都實現了filter //介面
filter.doFilter(request, response, this);
}
以下是Filter介面doFilter定義如下
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
過濾器鏈裡面的filter在呼叫dofilter完成後,會繼續呼叫chain.doFilter(request,response)方法,而這個chain其實就是applicationfilterchain,所以呼叫過程又回到了上面呼叫dofilter和呼叫internalDoFilter方法,這樣執行直到裡面的過濾器全部執行
當filte都呼叫完成後,它就會初始化相應的servlet,(例如jsp資源,預設它會開啟一個 JspServlet物件)
// 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);
舉個例子
假如訪問的是個jsp,首先開啟一個 JspServlet物件,然後該JspServlet物件會呼叫它的service方法
以下是個JspServlet的定義以及service方法的描述
public class JspServlet extends HttpServlet implements PeriodicEventListener
public void service (HttpServletRequest request,
HttpServletResponse response)
//jspFile may be configured as an init-param for this servlet instance //該jspUri代表訪問jsp的相對路徑
String jspUri = jspFile;
拿到該路徑後它就會去判斷該jsp是否已經預編譯過
boolean precompile = preCompile(request);
serviceJspFile(request, response, jspUri, null, precompile);
如果已經編譯則直接呼叫該jsp對應的servlet的_jspService方法
否則先編譯在呼叫
假如是個html頁面的訪問則直接呼叫DefaultServlet做相應的處理
簡單的分析了下,作為一個學習筆記吧