轉帖|其它|編輯:郝浩|2009-02-09 10:59:33.000|閱讀 1237 次
概述:Result機制——豐富您的視圖
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
Struts2將Result列為一個獨立的層次,可以說是整個Struts2的Action層架構設計中的另外一個精華所在。Result之所以成為一個層次,其實是為了解決MVC框架中,如何從Control層轉向View層這樣一個問題而存在的。所以,接下來我們詳細討論一下Result的方方面面。
Result的職責
Result作為一個獨立的層次存在,必然有其存在的價值,它也必須完成它所在的層次的職責。Result是為了解決如何從Control層轉向View層這樣一個問題而存在的,那么Result最大的職責,就是架起Action到View的橋梁。具體來說,我把這些職責大概分成以下幾個方面:
封裝跳轉邏輯
Result的首要職責,是封裝Action層到View層的跳轉邏輯。之前我們已經反復提到,Struts2的Action是一個與Web容器無關的POJO。所以,在Action執行完畢之后,框架需要把代碼的執行權重新交還給Web容器,并轉向到相應的頁面或者其他類型的View層。而這個跳轉邏輯,就由Result來完成。這樣,好處也是顯而易見的,對Action屏蔽任何Web容器的相關信息,使得每個層次更加清晰。
View層的顯示類型非常多,有最常見的JSP、當下非常流行的Freemarker/Velocity模板、Redirect到一個新的地址、文本流、圖片流、甚至是JSON對象等等。所以Result層的獨立存在,就能夠對這些顯示類型進行區分,并封裝合理的跳轉邏輯。
以JSP轉向為例,在Struts2自帶的ServletDispatcherResult中就存在著核心的JSP跳轉邏輯:
Java代碼
HttpServletRequest request = ServletActionContext.getRequest();
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
....
dispatcher.forward(request, response);
HttpServletRequest request = ServletActionContext.getRequest();
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
....
dispatcher.forward(request, response);
再以Redirect重定向為例,在Struts2自帶的ServletRedirectResult中,也同樣存在著重定向的核心代碼:
Java代碼
HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);
....
response.sendRedirect(finalLocation);
HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE);
....
response.sendRedirect(finalLocation);
由此可見,絕大多數的Result,都封裝了與Web容器相關的跳轉邏輯,由于這些邏輯往往需要和Servlet對象打交道,所以,遵循Struts2的基本原則,將它作為一個獨立的層次,從而將Action從Web容器中解放出來。
準備顯示數據
之前提到,View層的展現方式很多,除了傳統的JSP以外,還有類似Freemarker/Velocity這樣的模板。根據模板顯示的基本原理,需要將預先定義好的模板(Template)和需要展示的數據(Model)組織起來,交給模板引擎,才能夠正確顯示。而這部分工作,就由Result層來完成。
以Struts2自帶的FreemarkerResult為例,在Result中,就存在著為模板準備數據的邏輯代碼:
Java代碼
protected TemplateModel createModel() throws TemplateModelException {
ServletContext servletContext = ServletActionContext.getServletContext();
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ValueStack stack = ServletActionContext.getContext().getValueStack();
Object action = null;
if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException
return freemarkerManager.buildTemplateModel(stack, action, servletContext, request, response, wrapper);
}
protected TemplateModel createModel() throws TemplateModelException {
ServletContext servletContext = ServletActionContext.getServletContext();
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ValueStack stack = ServletActionContext.getContext().getValueStack();
Object action = null;
if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException
return freemarkerManager.buildTemplateModel(stack, action, servletContext, request, response, wrapper);
}
有興趣的讀者可以順著思路去看源碼,看看這些Result到底是如何獲取各種對象的值的。
控制輸出行為
有的時候,針對同一種類型的View展示,我們可能會有不同的輸出行為。具體來說,可能有時候,我們需要對輸出流指定特定的BufferSize、Encoding等等。Result層,作為一個獨立的層次,可以提供極大的擴展性,從而保證我們能夠定義自己期望的輸出類型。
以Struts2自帶的HttpHeaderResult為例:
Java代碼
public void execute(ActionInvocation invocation) throws Exception {
HttpServletResponse response = ServletActionContext.getResponse();
if (status != -1) {
response.setStatus(status);
}
if (headers != null) {
ValueStack stack = ActionContext.getContext().getValueStack();
for (Iterator iterator = headers.entrySet().iterator();
iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String value = (String) entry.getValue();
String finalValue = parse ? TextParseUtil.translateVariables(value, stack) : value;
response.addHeader((String) entry.getKey(), finalValue);
}
}
}
public void execute(ActionInvocation invocation) throws Exception {
HttpServletResponse response = ServletActionContext.getResponse();
if (status != -1) {
response.setStatus(status);
}
if (headers != null) {
ValueStack stack = ActionContext.getContext().getValueStack();
for (Iterator iterator = headers.entrySet().iterator();
iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String value = (String) entry.getValue();
String finalValue = parse ? TextParseUtil.translateVariables(value, stack) : value;
response.addHeader((String) entry.getKey(), finalValue);
}
}
}
我們可以在這里添加我們自定義的內容到HttpHeader中去,從而控制Http的輸出。 [SPAN]
Result的定義
讓我們來看看Result的接口定義:
Java代碼
public interface Result extends Serializable {
/**
* Represents a generic interface for all action execution results, whether that be displaying a webpage, generating
* an email, sending a JMS message, etc.
*/
public void execute(ActionInvocation invocation) throws Exception;
}
public interface Result extends Serializable {
/**
* Represents a generic interface for all action execution results, whether that be displaying a webpage, generating
* an email, sending a JMS message, etc.
*/
public void execute(ActionInvocation invocation) throws Exception;
}
這個接口定義非常簡單,通過傳入ActionInvocation,執行一段邏輯。我們來看看Struts2針對這個接口實現的一個抽象類,它規定了許多默認實現:
Java代碼
public abstract class StrutsResultSupport implements Result, StrutsStatics {
private static final Log _log = LogFactory.getLog(StrutsResultSupport.class);
/** The default parameter */
public static final String DEFAULT_PARAM = "location";
private boolean parse;
private boolean encode;
private String location;
private String lastFinalLocation;
public StrutsResultSupport() {
this(null, true, false);
}
public StrutsResultSupport(String location) {
this(location, true, false);
}
public StrutsResultSupport(String location, boolean parse, boolean encode) {
this.location = location;
this.parse = parse;
this.encode = encode;
}
// setter method 省略
/**
* Implementation of the <tt>execute</tt> method from the <tt>Result</tt> interface. This will call
* the abstract method {@link #doExecute(String, ActionInvocation)} after optionally evaluating the
* location as an OGNL evaluation.
*
* @param invocation the execution state of the action.
* @throws Exception if an error occurs while executing the result.
*/
public void execute(ActionInvocation invocation) throws Exception {
lastFinalLocation = conditionalParse(location, invocation);
doExecute(lastFinalLocation, invocation);
}
/**
* Parses the parameter for OGNL expressions against the valuestack
*
* @param param The parameter value
* @param invocation The action invocation instance
* @return The resulting string
*/
protected String conditionalParse(String param, ActionInvocation invocation) {
if (parse && param != null && invocation != null) {
return TextParseUtil.translateVariables(param, invocation.getStack(),
new TextParseUtil.ParsedValueEvaluator() {
public Object evaluate(Object parsedValue) {
if (encode) {
if (parsedValue != null) {
try {
// use UTF-8 as this is the recommended encoding by W3C to
// avoid incompatibilities.
return URLEncoder.encode(parsedValue.toString(), "UTF-8");
}
catch(UnsupportedEncodingException e) {
_log.warn("error while trying to encode ["+parsedValue+"]", e);
}
}
}
return parsedValue;
}
});
} else {
return param;
}
}
/**
* Executes the result given a final location (jsp page, action, etc) and the action invocation
* (the state in which the action was executed). Subclasses must implement this class to handle
* custom logic for result handling.
*
* @param finalLocation the location (jsp page, action, etc) to go to.
* @param invocation the execution state of the action.
* @throws Exception if an error occurs while executing the result.
*/
protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;
}
public abstract class StrutsResultSupport implements Result, StrutsStatics {
private static final Log _log = LogFactory.getLog(StrutsResultSupport.class);
/** The default parameter */
public static final String DEFAULT_PARAM = "location";
private boolean parse;
private boolean encode;
private String location;
private String lastFinalLocation;
public StrutsResultSupport() {
this(null, true, false);
}
public StrutsResultSupport(String location) {
this(location, true, false);
}
public StrutsResultSupport(String location, boolean parse, boolean encode) {
this.location = location;
this.parse = parse;
this.encode = encode;
}
// setter method 省略
/**
* Implementation of the <tt>execute</tt> method from the <tt>Result</tt> interface. This will call
* the abstract method {@link #doExecute(String, ActionInvocation)} after optionally evaluating the
* location as an OGNL evaluation.
*
* @param invocation the execution state of the action.
* @throws Exception if an error occurs while executing the result.
*/
public void execute(ActionInvocation invocation) throws Exception {
lastFinalLocation = conditionalParse(location, invocation);
doExecute(lastFinalLocation, invocation);
}
/**
* Parses the parameter for OGNL expressions against the valuestack
*
* @param param The parameter value
* @param invocation The action invocation instance
* @return The resulting string
*/
protected String conditionalParse(String param, ActionInvocation invocation) {
if (parse && param != null && invocation != null) {
return TextParseUtil.translateVariables(param, invocation.getStack(),
new TextParseUtil.ParsedValueEvaluator() {
public Object evaluate(Object parsedValue) {
if (encode) {
if (parsedValue != null) {
try {
// use UTF-8 as this is the recommended encoding by W3C to
// avoid incompatibilities.
return URLEncoder.encode(parsedValue.toString(), "UTF-8");
}
catch(UnsupportedEncodingException e) {
_log.warn("error while trying to encode ["+parsedValue+"]", e);
}
}
}
return parsedValue;
}
});
} else {
return param;
}
}
/**
* Executes the result given a final location (jsp page, action, etc) and the action invocation
* (the state in which the action was executed). Subclasses must implement this class to handle
* custom logic for result handling.
*
* @param finalLocation the location (jsp page, action, etc) to go to.
* @param invocation the execution state of the action.
* @throws Exception if an error occurs while executing the result.
*/
protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;
}
很顯然,這個默認實現是為那些類似JSP,Freemarker或者Redirect這樣的頁面跳轉的Result而準備的一個基類,它規定了Result將要跳轉到的具體頁面的位置、是否需要解析參數,等等。 [SPAN]
如果我們試圖編寫自定義的Result,我們可以實現Result接口,并在struts.xml中進行聲明:
Java代碼
public class CustomerResult implements Result {
public void execute(ActionInvocation invocation) throws Exception {
// write your code here
}
}
public class CustomerResult implements Result {
public void execute(ActionInvocation invocation) throws Exception {
// write your code here
}
}
Xml代碼
<result-type name="customerResult" class="com.javaeye.struts2.CustomerResult"/>
<result-type name="customerResult" class="com.javaeye.struts2.CustomerResult"/>
常用的Result
接下來,大致介紹一下Struts2內部已經實現的Result,并看看他們是如何工作的。
dispatcher
Xml代碼
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
dispatcher主要用于返回JSP,HTML等以頁面為基礎View視圖,這個也是Struts2默認的Result類型。在使用dispatcher時,唯一需要指定的,是JSP或者HTML頁面的位置,這個位置將被用于定位返回的頁面:
Xml代碼
<result name="success">/index.jsp</result>
<result name="success">/index.jsp</result>
而Struts2本身也沒有對dispatcher做出什么特殊的處理,只是簡單的使用Servlet API進行forward。
freemarker / velocity
Xml代碼
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
隨著模板技術的越來越流行,使用Freemarker或者Velocity模板進行View層展示的開發者越來越多。Struts2同樣為模板作為Result做出了支持。由于模板的顯示需要模板(Template)與數據(Model)的緊密配合,所以在Struts2中,這兩個Result的主要工作是為模板準備數據。
以Freemarker為例,我們來看看它是如何為模板準備數據的:
Java代碼
public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {
this.location = location;
this.invocation = invocation;
this.configuration = getConfiguration();
this.wrapper = getObjectWrapper();
// 獲取模板的位置
if (!location.startsWith("/")) {
ActionContext ctx = invocation.getInvocationContext();
HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
String base = ResourceUtil.getResourceBase(req);
location = base + "/" + location;
}
// 得到模板
Template template = configuration.getTemplate(location, deduceLocale());
// 為模板準備數據
TemplateModel model = createModel();
// 根據模板和數據進行輸出
// Give subclasses a chance to hook into preprocessing
if (preTemplateProcess(template, model)) {
try {
// Process the template
template.process(model, getWriter());
} finally {
// Give subclasses a chance to hook into postprocessing
postTemplateProcess(template, model);
}
}
}
public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException {
this.location = location;
this.invocation = invocation;
this.configuration = getConfiguration();
this.wrapper = getObjectWrapper();
// 獲取模板的位置
if (!location.startsWith("/")) {
ActionContext ctx = invocation.getInvocationContext();
HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST);
String base = ResourceUtil.getResourceBase(req);
location = base + "/" + location;
}
// 得到模板
Template template = configuration.getTemplate(location, deduceLocale());
// 為模板準備數據
TemplateModel model = createModel();
// 根據模板和數據進行輸出
// Give subclasses a chance to hook into preprocessing
if (preTemplateProcess(template, model)) {
try {
// Process the template
template.process(model, getWriter());
} finally {
// Give subclasses a chance to hook into postprocessing
postTemplateProcess(template, model);
}
}
}
從源碼中,我們可以看到,createModel()方法真正為模板準備需要顯示的數據。而之前,我們已經看到過這個方法的源碼,這個方法所準備的數據不僅包含ValueStack中的數據,還包含了被封裝過的HttpServletRequest,HttpSession等對象的數據。從而使得模板能夠以它特定的語法輸出這些數據。 [SPAN]
Velocity的Result也是類似,有興趣的讀者可以順著思路繼續深究源碼。
redirect
Xml代碼
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
如果你在Action執行完畢后,希望執行另一個Action,有2種方式可供選擇。一種是forward,另外一種是redirect。有關forward和redirect的區別,這里我就不再展開,這應該屬于Java程序員的基本知識。在Struts2中,分別對應這兩種方式的Result,就是chain和redirect。
先來談談redirect,既然是重定向,那么源地址與目標地址之間是2個不同的HttpServletRequest。所以目標地址將無法通過ValueStack等Struts2的特性來獲取源Action中的數據。如果你需要對目標地址傳遞參數,那么需要在目標地址url或者配置文件中指出:
Xml代碼
<!--
The redirect-action url generated will be :
/genReport/generateReport.jsp?reportType=pie&width=100&height=100
-->
<action name="gatherReportInfo" class="...">
<result name="showReportResult" type="redirect">
<param name="location">generateReport.jsp</param>
<param name="namespace">/genReport</param>
<param name="reportType">pie</param>
<param name="width">${width}</param>
<param name="height">${height}</param>
</result>
</action>
<!--
The redirect-action url generated will be :
/genReport/generateReport.jsp?reportType=pie&width=100&height=100
-->
<action name="gatherReportInfo" class="...">
<result name="showReportResult" type="redirect">
<param name="location">generateReport.jsp</param>
<param name="namespace">/genReport</param>
<param name="reportType">pie</param>
<param name="width">${width}</param>
<param name="height">${height}</param>
</result>
</action>
同時,Redirect的Result支持在配置文件中,讀取并解析源Action中ValueStack的值,并成為參數傳遞到Redirect的地址中。上面給出的例子中,width和height就是ValueStack中的值。
chain
Xml代碼
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
再來談談chain,之前提到,chain其實只是在一個action執行完畢之后,forward到另外一個action,所以他們之間是共享HttpServletRequest的。在使用chain作為Result時,往往會配合使用ChainingInterceptor。有關ChainingInterceptor,Struts2的Reference說明了其作用:
Struts2 Reference 寫道:If you need to copy the properties from your previous Actions in the chain to the current action, you should apply the ChainingInterceptor. The Interceptor will copy the original parameters from the request, and the ValueStack is passed in to the target Action. The source Action is remembered by the ValueStack, allowing the target Action to access the properties of the preceding Action(s) using the ValueStack, and also makes these properties available to the final result of the chain, such as the JSP or Velocity page.
也就是說,ChainingInterceptor的作用是在Action直接傳遞數據。事實上,源Action中ValueStack的數據會被做一次Copy,這樣,2個Action中的數據都在ValueStack中,使得對于前臺來說,通過ValueStack來取數據,是透明而共享的。chain這個Result有一些常用的使用情景,這點在Struts2的Reference中也有說明:
Struts2 Reference 寫道:One common use of Action chaining is to provide lookup lists (like for a dropdown list of states). Since these Actions get put on the ValueStack, their properties will be available in the view. This functionality can also be done using the ActionTag to execute an Action from the display page.
比如說,一張頁面中,你可能有許多數據要顯示,而某些數據的獲取方式可能被很多不同的頁面共享(典型來說,“推薦文章”這個小欄目的數據獲取,可能會被很多頁面所共享)。這種情況下,可以把這部分邏輯抽取到一個獨立Action中,并使用chain,將這個Action與主Action串聯起來。這樣,最后到達頁面的時候,頁面始終可以得到每個Action中的數據。
不過chain這種Result,是在使用時需要慎重考慮的一種Result:
Struts2 Reference 寫道:As a rule, Action Chaining is not recommended. First explore other options, such as the Redirect After Post technique.
而Struts2也做出了理由上的說明:
Struts2 Reference 寫道:Experience shows that chaining should be used with care. If chaining is overused, an application can turn into "spaghetti code". Actions should be treated as a Transaction Script, rather than as methods in a Business Facade. Be sure to ask yourself why you need to chain from one Action to another. Is a navigational issue, or could the logic in Action2 be pushed back to a support class or business facade so that Action1 can call it too?
Ideally, Action classes should be as short as possible. All the core logic should be pushed back to a support class or a business facade, so that Actions only call methods. Actions are best used as adapters, rather than as a class where coding logic is defined.
從實戰上將,使用chain作為Result也的確存在著上面所說的許多問題,我個人也是非常不推崇濫用這種Result。尤其是,對于使用Spring和Hibernate的朋友來說,如果你開啟OpenSessionInView模式,那么Hibernate的session是跟隨HttpServletRequest的,所以session在整個action鏈中共享。這會為我們的編程帶來極大的麻煩。因為我們知道Hibernate的session會保留一份一級緩存,在action鏈中,共享一級緩存無疑會為你的調試工作帶來很大的不方便。
所以,謹慎使用chain作為你的Result,應該成為一條最佳實踐。
stream
Xml代碼
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
StreamResult等價于在Servlet中直接輸出Stream流。這種Result被經常使用于輸出圖片、文檔等二進制流到客戶端。通過使用StreamResult,我們只需要在Action中準備好需要輸出的InputStream即可。
Xml代碼
<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">filename="document.pdf"</param>
<param name="bufferSize">1024</param>
</result>
<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">filename="document.pdf"</param>
<param name="bufferSize">1024</param>
</result>
同時,StreamResult支持許多參數,對輸出的Stream流進行參數控制。具體每個參數的作用,可以參考://struts.apache.org/2.0.14/docs/stream-result.html
其他
Struts2的高度可擴展性保證了許多自定義的Result可以通過插件的形式發布出來。比較著名的有JSONResult,JFreeChartResult等等。有興趣的讀者可以在Struts2的官方網站上找到它們,并選擇合適的加入到你的項目中去。
關于Result配置簡化的思考
Struts2的Result,解決了“如何從Control層轉向View層”的問題。不過看了上面介紹的這些由框架本身實現的Result,我們可以發現Result所涉及到的,基本上還停留在為Control層到View層搭建橋梁。
傳統的,我們需要通過配置文件,來指定Action執行完畢之后,到底執行什么樣的Result。不過在這樣一個到處呼吁簡化配置的年代,存在著許多方式,可以省略配置:
1. 使用Annotation
Struts2的一些插件提供了@Result和@Results的Annotation,可以通過Annotation來省略XML配置。具體請參考相關的文檔。
2. Codebehind插件
Struts2自帶了一個Codebehind插件(Struts2.1以后被合并到了其他的插件中)。Codebehind的基本思想是通過CoC的方式,使用命名約定來確定JSP等資源文件的位置。它通過實現了XWork的UnknownHandler接口,來實現當Struts2框架無法找到相應的Result時,如何進行處理的邏輯。具體文檔可以參考://struts.apache.org/2.0.14/docs/codebehind-plugin.html
大家可以在上面這兩種方式中任意選擇,國內著名的開源倡導者Springside也是采用了上述2種方法。在多數情況下,使用Codebehind,針對其他的一些Result使用Annotation進行配置,這樣可以在一定程度上簡化配置。
不過我本人對使用Annotation簡化配置的評價不高。因為實際上使用Annotation,只是將原本就非常簡單的配置,從xml文件中移動到java代碼中而已。就代碼量而言,本身并沒有減少。
在這里,我也在經常在思考,如何進行配置簡化,可以不寫Annotation,完全使用CoC的方式來指定Result。Codebehind在CoC方面已經做出了榜樣,只是Codebehind無法判別Result的類型,所以它只能支持dispatcher / freemarker / velocity這三種Result。所以Result的類型的判別,成為了阻礙簡化其配置CoC化的攔路虎。
前一段時間,曾經熱播一部電視劇《暗算》,其中的《看風》篇中數學家黃依依的一段話給了我靈感:
黃依依 寫道:開啟密鎖鑰匙的復雜化,是現代密碼發展的趨勢。但這種復雜化卻受到無線通訊本身的限制,尤其是距離遠、布點多的呈放射性的無線通訊,一般的密鑰總是要藏在報文中。
密鑰既然可以藏在報文中,那么Result的類型當然也能夠藏在ResultCode中。
Java代碼
return "success";
return "success";
這樣一個簡單的success作為ResultCode,是無法識別成復雜的Result類型的,我們需要設計一套更加有效的ResultCode,同時,Struts2能夠識別這些ResultCode,并得到相應的Result類型和Result實例。這樣,我們就可以借用Codebehind的實現方式,實現XWork的UnknownHandler接口,從而達到我們的目的。例如,我們規定ResultCode的解析規則:
success —— 使用codebehind的規則進行JSP,Freemarker模板的尋址
r:/user/list —— 返回一個redirect的Result,地址為/user/list
c:/user/list —— 返回一個chain的Result,地址為/user/list
j:user —— 返回一個JSON的Result,JSONResult的Root對象為user
s:inputStream-text/html —— 返回一個StreamResult,使用inputStream,并將contentType設置成text/html
以此類推,大家可以定義自己喜歡的ResultCode的格式,從而簡化配置。有了這樣的規則,也就有了后來的實現。具體解析這些ResultCode,并為他們構建Result實例的源碼,大家可以參考我的一個插件項目LightURL。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:JavaEye