Struts2 Result-type(封装Action层到View层的跳转逻辑)
dispatcher主要用于返回JSP,HTML等以页面为基础View视图,这个也是Struts2默认的Result类型。在使用dispatcher时,唯一需要指定的,是JSP或者HTML页面的位置,这个位置将被用于定位返回的页面:
Xml代码
<result name="success">/index.jsp</result>
而Struts2本身也没有对dispatcher做出什么特殊的处理,只是简单的使用Servlet API进行forward。
freemarker / velocity
Xml代码
<result-type name="freemarker" + 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" 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" 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流进行参数控制。具体每个参数的作用,可以参考:http://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时,如何进行处理的逻辑。具体文档可以参考:http://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";?
这样一个简单的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。
转载:http://www.blogjava.net/jzone/articles/322222.html