Struts2源碼閱讀(五)_FilterDispatcher核心控制器

Dispatcher已經在之前講過,這就好辦了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。

 

 
  1. public void init(FilterConfig filterConfig) throws ServletException {     
  2.     try {     
  3.         this.filterConfig = filterConfig;     
  4.         initLogging();     
  5.      //創建dispatcher,前面都已經講過囉      
  6.         dispatcher = createDispatcher(filterConfig);     
  7.         dispatcher.init();     
  8.      //注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader      
  9.         dispatcher.getContainer().inject(this);     
  10.         //StaticContentLoader在BeanSelectionProvider中已經被注入了依賴關係:DefaultStaticContentLoader      
  11.      //可以在struts-default.xml中的<bean>可以找到      
  12.         staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));     
  13.     } finally {     
  14.         ActionContext.setContext(null);     
  15.     }     
  16. }   

 

 

 
  1. //下面來看DefaultStaticContentLoader的setHostConfig      
  2.     public void setHostConfig(HostConfig filterConfig) {     
  3.           //讀取初始參數pakages,調用parse(),解析成類似/org/apache/struts2/static,/template的數組         
  4.         String param = filterConfig.getInitParameter("packages");     
  5.            //"org.apache.struts2.static template org.apache.struts2.interceptor.debugging static"      
  6.         String packages = getAdditionalPackages();     
  7.         if (param != null) {     
  8.             packages = param + " " + packages;     
  9.         }     
  10.         this.pathPrefixes = parse(packages);     
  11.         initLogging(filterConfig);     
  12.     }       

 

現在回去doFilter的方法,每當有一個Request,都會調用這些Filters的doFilter方法

 

 
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {     
  2.     
  3.     HttpServletRequest request = (HttpServletRequest) req;     
  4.     HttpServletResponse response = (HttpServletResponse) res;     
  5.     ServletContext servletContext = getServletContext();     
  6.     
  7.     String timerKey = "FilterDispatcher_doFilter: ";     
  8.     try {     
  9.     
  10.         // FIXME: this should be refactored better to not duplicate work with the action invocation      
  11.         //先看看ValueStackFactory所注入的實現類OgnlValueStackFactory      
  12.      //new OgnlValueStack      
  13.         ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();     
  14.         ActionContext ctx = new ActionContext(stack.getContext());     
  15.         ActionContext.setContext(ctx);     
  16.     
  17.         UtilTimerStack.push(timerKey);     
  18.     
  19.  //如果是multipart/form-data就用MultiPartRequestWrapper進行包裝      
  20. //MultiPartRequestWrapper是StrutsRequestWrapper的子類,兩者都是HttpServletRequest實現      
  21. //此時在MultiPartRequestWrapper中就會把Files給解析出來,用於文件上傳      
  22. //所有request都會StrutsRequestWrapper進行包裝,StrutsRequestWrapper是可以訪問ValueStack      
  23. //下面是參見Dispatcher的wrapRequest      
  24.    // String content_type = request.getContentType();      
  25.        //if(content_type!= null&&content_type.indexOf("multipart/form-data")!=-1){      
  26.        //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);      
  27.        //request =new MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));      
  28.        //} else {      
  29.        //     request = new StrutsRequestWrapper(request);      
  30.        // }      
  31.          
  32.         request = prepareDispatcherAndWrapRequest(request, response);     
  33.         ActionMapping mapping;     
  34.         try {     
  35.          //根據url取得對應的Action的配置信息      
  36.          //看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中      
  37.             mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());     
  38.         } catch (Exception ex) {     
  39.             log.error("error getting ActionMapping", ex);     
  40.             dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);     
  41.             return;     
  42.         }     
  43.     
  44.      //如果找不到對應的action配置,則直接返回。比如你輸入***.jsp等等                                       
  45.      //這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數packages配置的包路徑去查找對應的靜態資源並輸出到頁面流中,當然.class文件除外。如果再沒有則跳轉到404        
  46.         if (mapping == null) {     
  47.             // there is no action in this request, should we look for a static resource?      
  48.             String resourcePath = RequestUtils.getServletPath(request);     
  49.     
  50.             if ("".equals(resourcePath) && null != request.getPathInfo()) {     
  51.                 resourcePath = request.getPathInfo();     
  52.             }     
  53.     
  54.             if (staticResourceLoader.canHandle(resourcePath)) {     
  55.             // 在DefaultStaticContentLoader中:return serveStatic && (resourcePath.startsWith("/struts") || resourcePath.startsWith("/static"));      
  56.                 staticResourceLoader.findStaticResource(resourcePath, request, response);     
  57.             } else {     
  58.                 // this is a normal request, let it pass through      
  59.                 chain.doFilter(request, response);     
  60.             }     
  61.             // The framework did its job here      
  62.             return;     
  63.         }     
  64.         //正式開始Action的方法      
  65.         dispatcher.serviceAction(request, response, servletContext, mapping);     
  66.     
  67.     } finally {     
  68.         try {     
  69.             ActionContextCleanUp.cleanUp(req);     
  70.         } finally {     
  71.             UtilTimerStack.pop(timerKey);     
  72.         }     
  73.     }     
  74. }   

 

 

 
  1. //下面是ActionMapper接口的實現類 DefaultActionMapper的getMapping()方法的源代碼:      
  2.     public ActionMapping getMapping(HttpServletRequest request,     
  3.             ConfigurationManager configManager) {     
  4.         ActionMapping mapping = new ActionMapping();     
  5.         String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do      
  6.     
  7.     
  8.         int indexOfSemicolon = uri.indexOf(";");//修正url的帶;jsessionid 時找不到而且的bug      
  9.         uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;     
  10.     
  11.         uri = dropExtension(uri, mapping);//刪除擴展名,默認擴展名爲action      
  12.         if (uri == null) {     
  13.             return null;     
  14.         }     
  15.     
  16.         parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace      
  17.     
  18.         handleSpecialParameters(request, mapping);//去掉重複參數      
  19.     
  20.         //如果Action的name沒有解析出來,直接返回      
  21.     if (mapping.getName() == null) {     
  22.       returnnull;     
  23.     }     
  24.      //下面處理形如testAction!method格式的請求路徑      
  25.     if (allowDynamicMethodCalls) {     
  26.       // handle "name!method" convention.      
  27.       String name = mapping.getName();     
  28.       int exclamation = name.lastIndexOf("!");//!是Action名稱和方法名的分隔符      
  29.       if (exclamation != -1) {     
  30.         mapping.setName(name.substring(0, exclamation));//提取左邊爲name      
  31.         mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method      
  32.       }     
  33.     }     
  34.     
  35.         return mapping;     
  36.     }   

 

從代碼中看出,getMapping()方法返回ActionMapping類型的對象,該對象包含三個參數:Action的name、namespace和要調用的方法method。
  如果getMapping()方法返回ActionMapping對象爲null,則FilterDispatcher認爲用戶請求不是Action,自然另當別論,FilterDispatcher會做一件非常有意思的事:如果請求以/struts開頭,會自動查找在web.xml文件中配置的 packages初始化參數,就像下面這樣:

 

 
  1.  <filter>    
  2.         <filter-name>struts2</filter-name>    
  3.         <filter-class>    
  4.           org.apache.struts2.dispatcher.FilterDispatcher     
  5.         </filter-class>    
  6.         <init-param>    
  7.           <param-name>packages</param-name>    
  8.           <param-value>com.lizanhong.action</param-value>    
  9.         </init-param>    
  10.     </filter>    

 

FilterDispatcher會將com.lizanhong.action包下的文件當作靜態資源處理,即直接在頁面上顯示文件內容,不過會忽略擴展名爲class的文件。比如在com.lizanhong.action包下有一個aaa.txt的文本文件,其內容爲“中華人民共和國”,訪問 http://localhost:8081/Struts2Demo/struts/aaa.txt時會輸出txt中的內容
   FilterDispatcher.findStaticResource()方法

 

 
  1. protectedvoid findStaticResource(String name, HttpServletRequest request, HttpServletResponse response) throws IOException {     
  2.     if (!name.endsWith(".class")) {//忽略class文件      
  3.       //遍歷packages參數      
  4.       for (String pathPrefix : pathPrefixes) {     
  5.         InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流      
  6.         if (is != null) {     
  7.           ...     
  8.           // set the content-type header      
  9.           String contentType = getContentType(name);//讀取內容類型      
  10.           if (contentType != null) {     
  11.             response.setContentType(contentType);//重新設置內容類型      
  12.           }     
  13.          ...     
  14.           try {     
  15.            //將讀取到的文件流以每次複製4096個字節的方式循環輸出      
  16.             copy(is, response.getOutputStream());     
  17.           } finally {     
  18.             is.close();     
  19.           }     
  20.           return;     
  21.         }     
  22.       }     
  23.     }     
  24.   }    

 

如果用戶請求的資源不是以/struts開頭——可能是.jsp文件,也可能是.html文件,則通過過濾器鏈繼續往下傳送,直到到達請求的資源爲止。
如果getMapping()方法返回有效的ActionMapping對象,則被認爲正在請求某個Action,將調用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關鍵所在。
下面就來看serviceAction,這又回到全局變量dispatcher中了

 

 
  1. //Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.      
  2. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,     
  3.                               ActionMapping mapping) throws ServletException {     
  4.         //createContextMap方法主要把Application、Session、Request的key value值拷貝到Map中      
  5.         Map<String, Object> extraContext = createContextMap(request, response, mapping, context);     
  6.     
  7.         // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action      
  8.         ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);     
  9.         boolean nullStack = stack == null;     
  10.         if (nullStack) {     
  11.             ActionContext ctx = ActionContext.getContext();     
  12.             if (ctx != null) {     
  13.                 stack = ctx.getValueStack();     
  14.             }     
  15.         }     
  16.         if (stack != null) {     
  17.             extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));     
  18.         }     
  19.     
  20.         String timerKey = "Handling request from Dispatcher";     
  21.         try {     
  22.             UtilTimerStack.push(timerKey);     
  23.             String namespace = mapping.getNamespace();     
  24.             String name = mapping.getName();     
  25.             String method = mapping.getMethod();     
  26.     
  27.             Configuration config = configurationManager.getConfiguration();     
  28.             //創建一個Action的代理對象,ActionProxyFactory是創建ActionProxy的工廠      
  29.             //參考實現類:DefaultActionProxy和DefaultActionProxyFactory      
  30.             ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(     
  31.                     namespace, name, method, extraContext, truefalse);     
  32.     
  33.             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());     
  34.     
  35.             // if the ActionMapping says to go straight to a result, do it!      
  36.             //如果是Result,則直接轉向,關於Result,ActionProxy,ActionInvocation下一講中再分析      
  37.             if (mapping.getResult() != null) {     
  38.                 Result result = mapping.getResult();     
  39.                 result.execute(proxy.getInvocation());     
  40.             } else {     
  41.                 //執行Action      
  42.                 proxy.execute();     
  43.             }     
  44.     
  45.             // If there was a previous value stack then set it back onto the request      
  46.             if (!nullStack) {     
  47.                 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);     
  48.             }     
  49.         } catch (ConfigurationException e) {     
  50.             // WW-2874 Only log error if in devMode      
  51.             if(devMode) {     
  52.                 LOG.error("Could not find action or result", e);     
  53.             }     
  54.             else {     
  55.                 LOG.warn("Could not find action or result", e);     
  56.             }     
  57.             sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);     
  58.         } catch (Exception e) {     
  59.             sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);     
  60.         } finally {     
  61.             UtilTimerStack.pop(timerKey);     
  62.         }     
  63.     }    

 

Come From: http://qidaoxp.javaeye.com/blog/501088

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章