轉自: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做相應的處理
簡單的分析了下,作爲一個學習筆記吧