FilterDispatcher處理流程

 

9.4  Struts 2的核心機制

Struts 2的處理流程已經和Struts 1大相徑庭了,但是和WebWork比較相似,這都是因爲Struts 2和WebWork合併的緣故,並吸取了WebWork大部分設計思想。下面講解Struts 2的核心流程,以及其他一些處理機制。

9.4.1  FilterDispatcher處理流程

在Struts 2中,最重要的一個類是org.apache.struts2.dispatcher.FilterDispatcher,從前面的示例可以看出,用戶通過瀏覽器提交一個(HttpServletRequest)請求後,請求被在web.xml中定義的過濾器FilterDispatcher攔截,在FilterDispatcher中主要經過大概3層過濾器的處理,分別是ActionContext CleanUp、其他過濾器(Othter Filters、SiteMesh等)、FilterDispatcher。

在FilterDispatcher過濾器中首先詢問ActionMapper是否需要調用某個Action來處理請求,如果ActionMapper決定需要調用某個Action,FilterDispatcher則把請求的處理交給ActionProxy,ActionProxy通過配置文件struts.xml找到需要調用的Action類,然後ActionProxy創建一個ActionInvocation實例並調用該Action,但在調用之前,ActionInvocation會根據配置加載Action相關的所有Interceptor,等Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果result。

在詳細介紹FilterDispatcher之前,先講解一下Servlet中過濾器的概念,以使讀者對此有一個深入的認識。過濾器提供一種面向對象的模塊化機制,用以將公共任務封裝到可插入的組件中,這些組件通過一個配置文件來聲明並動態地處理。實現一個過濾器需要3個步驟:首先編寫過濾器實現類的程序,然後把該過濾器添加到web.xml 中聲明,最後把過濾器與應用程序一起打包並部署。

過濾器 API 一共包含 3 個簡單的接口:Filter、FilterChain 和 FilterConfig。過濾器類必須實現 Filter 接口:

init():這個方法在容器實例化過濾器時被調用,它主要設計用於使過濾器爲處理做準備。容器爲這個方法傳遞一個FilterConfig,其中包含有配置信息。

doFilter():與Servlet擁有一個service()方法來處理請求一樣,過濾器擁有單個用於處理請求和響應的方法doFilter()。這個方法接收3個輸入參數: ServletRequest、ServletResponse和FilterChain。FilterChain對於正確的過濾操作至關重要,doFilter()方法必須調用FilterChain的doFilter()方法,除非該方法用來攔截以後的下游處理。

destroy():該方法由容器在銷燬過濾器實例之前調用,以便能夠執行任何必需的清理代碼。

過濾器通過 web.xml 文件中的兩個XML標籤來聲明。<filter>標籤定義過濾器的名稱,並且聲明實現類和init()參數。<filter-mapping>標籤將過濾器與Servlet或URL模式相關聯。<filter>標籤負責把一個過濾器名和一個特定的類關聯起來,這種關聯是通過<filter-name>和<filter-class>元素指定的。<filter>必須有一個<ulr-pattern>或者<servlet-name>元素,可以通過<ulr-pattern>來指定通配符,將過濾器應用到Web資源範圍;也可以通過<servlet-name>將過濾器指定到某一個特定的Servlet上。應該注意這些聲明的順序,所引用的過濾器名必須在前面的過濾器定義中給出。下面給出一個過濾器配置的示例代碼。

  1. <!--編碼過濾器-->  
  2. <filter>  
  3. <filter-name>SetCharacterEncoding</filter-name>  
  4. <filter-class>  
  5. com.gd.web.filter.GdSetCharacterEncodingFilter  
  6. </filter-class>  
  7. <init-param>  
  8. <param-name>encoding</param-name>  
  9. <param-value>GBK</param-value>  
  10. </init-param>  
  11. <init-param>  
  12. <param-name>ignore</param-name>  
  13. <param-value>true</param-value>  
  14. </init-param>  
  15. </filter>  
  16. <!--過濾所有的訪問-->  
  17. <filter-mapping>  
  18. <filter-name>SetCharacterEncoding</filter-name>  
  19. <url-pattern>/*</url-pattern>  
  20. </filter-mapping> 

然也可以配置多個過濾器,多個過濾器將按照配置的順序執行。

通過上面的介紹,相信讀者對過濾器會有一個深入的瞭解。打開FilterDispatcher的源代碼可以看到,FilterDispatcher也同樣遵循這樣的原則,同樣實現了init()、doFilter ()、destroy()這3個接口,在init()接口裏主要實現了創建Dispatcher和設置默認包的功能,示例代碼如下:

  1. public void init(FilterConfig filterConfig) throws ServletException {  
  2. try {  
  3. this.filterConfig = filterConfig;  
  4. //初始化日誌  
  5. initLogging();  
  6. //創建一個Dispatcher  
  7. dispatcher = createDispatcher(filterConfig);  
  8. dispatcher.init();  
  9. dispatcher.getContainer().inject(this);  
  10. //設定默認包  
  11. staticResourceLoader.setHostConfig(new  FilterHostConfig(filterConfig));  
  12. finally {  
  13. ActionContext.setContext(null);  
  14. }  
  15. }  
  16. 在destroy()接口裏主要實現了銷燬Dispatcher和上下文的功能,示例代碼如下:  
  17. public void destroy() {  
  18. //如果Dispatcher爲空,則正常結束  
  19. if (dispatcher == null) {  
  20. log.warn("something is seriously wrong,  Dispatcher is not initialized (null) ");  
  21. else {  
  22. try {  
  23. //銷燬Dispatcher  
  24. dispatcher.cleanup();  
  25. finally {  
  26. ActionContext.setContext(null);  
  27. }  
  28. }  

在doFilter()接口裏主要實現了創建Dispatcher和設置默認包的功能,示例代碼如下:

  1. public void doFilter(ServletRequest req, S ervletResponse res, FilterChain chain) throws   
  2. IOException, ServletException {  
  3. //獲取用戶請求的request  
  4. HttpServletRequest request = (HttpServletRequest) req;  
  5. HttpServletResponse response = (HttpServletResponse) res;  
  6. ServletContext servletContext = getServletContext();  
  7. //加上時間戳  
  8. String timerKey = "FilterDispatcher_doFilter: ";  
  9. try {  
  10. //設定上下文或棧  
  11. ValueStack stack = dispatcher.getContainer(). getInstance(ValueStackFactory.class).  
  12. createValueStack();  
  13. ActionContext ctx = new ActionContext(stack.getContext());  
  14. ActionContext.setContext(ctx);  
  15. //重新封裝request,記錄使用的語言、編碼方式、是否是上傳文件等  
  16. UtilTimerStack.push(timerKey);  
  17. request = prepareDispatcherAndWrapRequest(request, response);  
  18. //獲取ActionMapping  
  19. ActionMapping mapping;  
  20. try {  
  21. mapping = actionMapper.getMapping(request,  dispatcher.getConfigurationManager());  
  22. catch (Exception ex) {  
  23. //如果沒有找到合適的ActionMapping,則拋出異常  
  24. dispatcher.sendError(request, response, servletContext, HttpServletResponse.  
  25. SC_INTERNAL_SERVER_ERROR, ex);  
  26. return;  
  27. }  
  28. //如果沒有配置ActionMapping,則判斷是否爲靜態資源  
  29. if (mapping == null) {  
  30. //獲取訪問請求的路徑  
  31. String resourcePath = RequestUtils.getServletPath(request);  
  32. if ("".equals(resourcePath) && null != request.getPathInfo()) {  
  33. resourcePath = request.getPathInfo();  
  34. }  
  35. //判斷是否爲靜態資源  
  36. if (staticResourceLoader.canHandle(resourcePath)) {  
  37. staticResourceLoader.findStaticResource(resourcePath, request, response);  
  38. else {  
  39. // 如果不是,則繼續執行下一個過濾器  
  40. chain.doFilter(request, response);  
  41. }  
  42. return;  
  43. }  
  44. //在正常情況下,調用serviceAction方法  
  45. dispatcher.serviceAction(request, response, servletContext, mapping);  
  46. //清空上下文和時間戳  
  47. finally {  
  48. try {  
  49. ActionContextCleanUp.cleanUp(req);  
  50. finally {  
  51. UtilTimerStack.pop(timerKey);  
  52. }  
  53. }  
  54. =========================================================================
  55. http://book.51cto.com/art/200912/169040.htm
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章