本章簡言 |
上一章筆者講到關於DefaultActionInvocation類執行action的相關知識。我們清楚的知道在執行action類實例之后會相關處理返回的結果。而這章筆者將對處理結果相關的內容進行講解。筆者叫他們為Result類實例。如果還記得在上一章最后筆者說可以把處理action執行的結果簡單的理解處理網頁。而且還用紅色標識。實際是處理跟表現層有關的內容。而不是頁面上的內容。如HTML。即是MVC里面的C到V的內容。當然這還關系到配置文件里面的result元素節點信息。如strtus.xml文件等里面的action元素節點下的result信息。
Result類實例的來源 |
我們都知道action節點下會很多result節點。在筆者的腦中好像result節點常用的有倆個屬性節點:name和type。那么result元素節點下到底有些什么呢?這就不得不去看一下相關的DTD文件。請找到對應的struts-2.5.dtd文件。查看一下下面的內容。
<!ELEMENT result (#PCDATA|param)*> <!ATTLIST result name CDATA #IMPLIED type CDATA #IMPLIED >
好了。好像只有上面講到的倆個屬性節點。但是可以看到他還有一個子節點param。那么為什么要講到result節點呢?主要是上一章出筆者講到action類實例執行之后,會去處理結果。哪怕是返回String類型的結果,最終也會獲得對應的Result類實例並執行處理結果。關於Result接口的源碼卻顯的非常的簡單。如下
1 public interface Result extends Serializable { 2 3 public void execute(ActionInvocation invocation) throws Exception; 4 5 }
使用過struts2的人都知道result節點的type值有很多種。type值更是用來表示返回結果的類型。即是type值有多少種,返回的結果就有多少種。如果讓筆者來記的話。說真的筆者輸了。筆者主要是通過源碼文件才能知道到底有多少種。找到源碼文件struts-default.xml查看里面的package元素節點下的result-types節點。如下
<result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.result.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.result.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.result.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.result.ServletActionRedirectResult"/> <result-type name="stream" class="org.apache.struts2.result.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.result.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.result.PlainTextResult" /> <result-type name="postback" class="org.apache.struts2.result.PostbackResult" /> </result-types>
好吧。struts2為我們提供了11種。認真的查看源碼的人會看到一個屬性default。如上面的name為“dispatcher”的result-types的default屬性值為true。實際這個值表示為默認使用dispatcher類型的返回結果。同時我們也看到每一個類型對應的類。這個時候筆者就會有一個問題。action執行之后,他是什么樣根據String類型的結果值找到對應的Result類實例。筆者就不得不去找上一章解了到的源碼。即是createResult方法的源碼。如果沒有記錯的話。從createResult方法我們知道Result類實例是通過ObjectFactory類的buildResult方法得到的。對應的源碼如下
ObjectFactory類:
public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception { return resultFactory.buildResult(resultConfig, extraContext); }
看到上面的代碼我們就清楚要找ResultFactory接口的實例類。那么實現ResultFactory接口的類有StrutsResultFactory類和DefaultResultFactory類。到底是哪一個呢?這個問題當年筆者找了很久就是不明白他到底是什么知道用哪一個。當年我去查看了初始化Container容器時,發現好像關於了ResultFactory接口對應的類,只有注入一個DefaultResultFactory類。如下源碼。可是項目運行起來跟蹤下去。發現用的是StrutsResultFactory類實例。筆者悶了很久。按道理應該是DefaultResultFactory類實例。可是為什么用的是StrutsResultFactory類呢?后來又在struts-default.xml文件里面找到一個。即是StrutsResultFactory類。如下源嗎。
DefaultConfiguration類的createBootstrapContainer方法。
builder.factory(ResultFactory.class, DefaultResultFactory.class, Scope.SINGLETON);
struts-default.xml文件:
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
看過XmlConfigurationProvider類的源碼就明白這邊ResultFactory接口注入的名字值是“struts”。而前面初始化Container容器注入的名字值是“default”。所以Container容器里面有倆個關於ResultFactory接口的注冊類。看樣子光是找到了struts-default.xml文件里面ResultFactory接口對應的StrutsResultFactory類。還是不能說明為什么要用StrutsResultFactory類實例。另一個原因是因為筆者認為應該是調用名字值為“default”的注冊類。為什么這樣子講呢?在於ObjectFactory類里面,setResultFactory方法上面用的是@Inject。即是用默認名字值為default的注冊類。筆者也不清楚到底是經過多少時間。最后在DefaultBeanSelectionProvider類的源碼里面找到了原因。原來在Container容器里面,名字值為default的注冊類被名字值為struts的注冊類替換了。源碼請讀者自己行查看吧。
既然ObjectFactory類可以用來生成對應的Result類實例。那么他做了些什么呢?這是筆者想要了解的關鍵點。上面ObjectFactory類的buildResult方法源碼里面我們至少知道他是在調用了StrutsResultFactory類實例。現在我們只要去找StrutsResultFactory類源碼就可以明白他做了什么。如下
1 public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception { 2 String resultClassName = resultConfig.getClassName(); 3 Result result = null; 4 5 if (resultClassName != null) { 6 result = (Result) objectFactory.buildBean(resultClassName, extraContext); 7 Map<String, String> params = resultConfig.getParams(); 8 if (params != null) { 9 setParameters(extraContext, result, params); 10 } 11 } 12 return result; 13 }
我們可以看出首先他是根據result節點的配置信息來獲得對應的類名。即是上面講到的11種結果類型對應的類。然后生成Result類實例。哎!又跑回ObjectFactory類了。這次卻是生成對象。在把對象轉化為Result類實例。接着就是設置對應的參數了。最后返回Result類實例。
Result類實例的處理 |
經過上面的過程之后。我們可以得到對應的Result類實例。相信大家都知道Result接口對的類實例就有11種。筆者當然也不可能在這里把11種都哪出來講。筆者只能把一些比較常用的Result類實例拿出來講。
1.ServletDispatcherResult類是strust2默認的返回結果。即是dispatcher類型。ServletDispatcherResult類實現於StrutsResultSupport類。讓我們一下StrutsResultSupport類的源碼吧。知道他做了些什么。如下
1 public void execute(ActionInvocation invocation) throws Exception { 2 lastFinalLocation = conditionalParse(location, invocation); 3 doExecute(lastFinalLocation, invocation); 4 }
上面就是Result類實例執行的入口方法。StrutsResultSupport類把當前location進行了處理。也是把要轉跳到哪一個網頁。其中conditionalParse方法是用於處理表達式。如動態返回結果。不管如何最后是一個跳轉的URL。然后調用doExecute抽象方法。好了。讓我們看一下ServletDispatcherResult類doExecute方法的源碼。
1 public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { 2 LOG.debug("Forwarding to location: {}", finalLocation); 3 4 PageContext pageContext = ServletActionContext.getPageContext(); 5 6 if (pageContext != null) { 7 pageContext.include(finalLocation); 8 } else { 9 HttpServletRequest request = ServletActionContext.getRequest(); 10 HttpServletResponse response = ServletActionContext.getResponse(); 11 RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation); 12 13 //處理跳轉URL上面的參數。並把他增加action請求的參數集合里面。 14 if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) { 15 String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1); 16 Map<String, Object> parameters = getParameters(invocation); 17 Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true); 18 if (queryParams != null && !queryParams.isEmpty()) 19 parameters.putAll(queryParams); 20 } 21 22 // 如果不存在網址的話,就跳出404 23 if (dispatcher == null) { 24 response.sendError(404, "result '" + finalLocation + "' not found"); 25 return; 26 } 27 28 //是否是一個action tag 就是網頁里面的一個包含action 29 Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE); 30 31 //最后跳轉 32 if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) { 33 request.setAttribute("struts.view_uri", finalLocation); 34 request.setAttribute("struts.request_uri", request.getRequestURI()); 35 36 dispatcher.forward(request, response); 37 } else { 38 dispatcher.include(request, response); 39 } 40 } 41 }
這段代碼很簡單,處理相關的參數。最后跳轉配置里面對應的網頁。筆者就不多說了。
2.ServletRedirectResult類是也是Result接口的實例類。即是redirect類型。相信看名字就可以知道用於重定向。也實現於StrutsResultSupport類。筆者也看了源碼。真不知道要講些什么。主要是判斷要重定向的finalLocation是不是完全的URL。如果不是就生成對應的完全URL。然后在去找result節點的參數,並把他的參數一起放到requestParameters集合里面。這樣子下面才可以生成對應的帶參數的URL。如http://xxxx/sss?xxx=xxx。最后重定向。
ServletRedirectResult類:
1 protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { 2 ActionContext ctx = invocation.getInvocationContext(); 3 HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); 4 HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE); 5 6 //判斷是否為完全的URL。true不是完全的URL。 7 //如果不是完全的URL就生成完全的URL 8 if (isPathUrl(finalLocation)) { 9 if (!finalLocation.startsWith("/")) { 10 ActionMapping mapping = actionMapper.getMapping(request, Dispatcher.getInstance().getConfigurationManager()); 11 String namespace = null; 12 if (mapping != null) { 13 namespace = mapping.getNamespace(); 14 } 15 16 if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) { 17 finalLocation = namespace + "/" + finalLocation; 18 } else { 19 finalLocation = "/" + finalLocation; 20 } 21 } 22 23 // if the URL's are relative to the servlet context, append the servlet context path 24 if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) { 25 finalLocation = request.getContextPath() + finalLocation; 26 } 27 } 28 29 //把result節點上的參數一起放到請求參數集合里面, 30 ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode()); 31 if (resultConfig != null) { 32 Map<String, String> resultConfigParams = resultConfig.getParams(); 33 34 List<String> prohibitedResultParams = getProhibitedResultParams(); 35 for (Map.Entry<String, String> e : resultConfigParams.entrySet()) { 36 if (!prohibitedResultParams.contains(e.getKey())) { 37 Collection<String> values = conditionalParseCollection(e.getValue(), invocation, suppressEmptyParameters); 38 if (!suppressEmptyParameters || !values.isEmpty()) { 39 requestParameters.put(e.getKey(), values); 40 } 41 } 42 } 43 } 44 45 //根據上面的參數來生成帶有參數的URL, 46 StringBuilder tmpLocation = new StringBuilder(finalLocation); 47 urlHelper.buildParametersString(requestParameters, tmpLocation, "&"); 48 49 // add the anchor 50 if (anchor != null) { 51 tmpLocation.append('#').append(anchor); 52 } 53 54 finalLocation = response.encodeRedirectURL(tmpLocation.toString()); 55 56 LOG.debug("Redirecting to finalLocation: {}", finalLocation); 57 58 sendRedirect(response, finalLocation);//重定向到對應的URL 59 }
3.ServletActionRedirectResult類是ServletRedirectResult類的一個子類。 即是redirectAction類型。這個類的做法就是把相應的action的URL處理好。在把URL轉給子類ServletRedirectResult去處理。當然要去獲得對應的action名字,空間命名,方法。然后生成完全的action的URL。
1 public void execute(ActionInvocation invocation) throws Exception { 2 actionName = conditionalParse(actionName, invocation); 3 if (namespace == null) { 4 namespace = invocation.getProxy().getNamespace(); 5 } else { 6 namespace = conditionalParse(namespace, invocation); 7 } 8 if (method == null) { 9 method = ""; 10 } else { 11 method = conditionalParse(method, invocation); 12 } 13 14 String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null)); 15 16 setLocation(tmpLocation); 17 18 super.execute(invocation); 19 }
筆者就介紹這三個類吧。相信讀者也知道要去找哪里類了。我們可以看到action類實例執行結束之后。就會去獲得對應的Result類實例。當然這里Result類實例具體是哪個類。就有result節點上的配置信息來決定了。然后進行要應的處理。
本章總結 |
本章講到Result類實例的知識點。知道了struts2內部有幾種的結果類型。每一個類型是什么處理得到的結果信息。是跳轉,還是重定向,一切都是strust.xml文件的result節點的信息了。