Struts2
将
Result
列为一个独立的层次,可以说是整个
Struts2
的
Action
层架构设计中的另外一个精华所在。
Result
之所以成为一个层次,其实
是为了解决
MVC
框架中,如何从
Control
层转向
View
层这样一个问题而存在的
在
struts2-core.jar/struts-default.xml
中,我们可以找到关于
result-type
的一些配置信息,从中可以看出
struts2
组件默认为我们提供了这
些
result-type
<
result-types
>
<
result-type
name
=
"chain"
class
=
"com.opensymphony.xwork2.ActionChainResult"
/>
<
result-type
name
=
"dispatcher"
class
=
"org.apache.struts2.dispatcher.ServletDispatcherResult"
default
=
"true"
/>
<
result-type
name
=
"freemarker"
class
=
"org.apache.struts2.views.freemarker.FreemarkerResult"
/>
<
result-type
name
=
"httpheader"
class
=
"org.apache.struts2.dispatcher.HttpHeaderResult"
/>
<
result-type
name
=
"redirect"
class
=
"org.apache.struts2.dispatcher.ServletRedirectResult"
/>
<
result-type
name
=
"redirectAction"
class
=
"org.apache.struts2.dispatcher.ServletActionRedirectResult"
/>
<
result-type
name
=
"stream"
class
=
"org.apache.struts2.dispatcher.StreamResult"
/>
<
result-type
name
=
"velocity"
class
=
"org.apache.struts2.dispatcher.VelocityResult"
/>
<
result-type
name
=
"xslt"
class
=
"org.apache.struts2.views.xslt.XSLTResult"
/>
<
result-type
name
=
"plainText"
class
=
"org.apache.struts2.dispatcher.PlainTextResult"
/>
<!--
Deprecated
name form scheduled for removal in Struts 2.1.0. The camelCase
versions are preferred. See ww
-1707 -->
<
result-type
name
=
"redirect-action"
class
=
"org.apache.struts2.dispatcher.ServletActionRedirectResult"
/>
<
result-type
name
=
"plaintext"
class
=
"org.apache.struts2.dispatcher.PlainTextResult"
/>
</
result-types
>
封装跳转逻辑
Result
的首要职责,是封装
Action
层到
View
层的跳转逻辑。之前我们已经反复提到,
Struts2
的
Action
是一个与
Web
容器无关的
POJO
。所以,在
Action
执行完毕之后,框架需要把代码的执行权重新交还给
Web
容器,并转向到相应的页面或者其他类型的
View
层。而这个跳转逻辑,就由
Result
来完成。这样,好处也是显而易见的,对
Action
屏蔽任何
Web
容器的相关信息,使得每个层次更加清晰。
View
层的显示类型非常多,有最常见的
JSP
、当下非常流行的
Freemarker/Velocity
模板、
Redirect
到一个新的地址、文本流、图片流、甚至是
JSON
对象等等。所以
Result
层的独立存在,就能够对这些显示类型进行区分,并封装合理的跳转逻辑。
以
JSP
转向为例,在
Struts2
自带的
ServletDispatcherResult
中就存在着核心的
JSP
跳转逻辑:
常用的
Result
接下来,大致介绍一下
Struts2
内部已经实现的
Result
,并看看他们是如何工作的。
dispatcher
Xml
代码
<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>
而
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"/>
随着模板技术的越来越流行,使用
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"/>
如果你在
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>
同时,
Redirect
的
Result
支持在配置文件中,读取并解析源
Action
中
ValueStack
的值,并成为参数传递到
Redirect
的地址中。上面给出的例子中,
width
和
height
就是
ValueStack
中的值。
chain
Xml
代码
<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"/>
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>
同时,
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
相关推荐
总的来说,Struts2 Action是连接用户请求与业务逻辑的桥梁,它封装了处理请求的具体行为,并通过配置文件和Result Codes来指导应用程序的流程控制。了解和熟练掌握Action的使用是开发Struts2应用的关键。
总之,"struts2---jar包"是构建基于Struts2框架的应用所必不可少的组件,它封装了大量的功能,让开发者能够专注于业务逻辑的实现,而不是底层的细节。通过深入理解和熟练使用Struts2,你可以创建出高效、可维护的Web...
9. **异常处理**:Struts 2允许开发者定义全局和局部的异常映射,当Action执行抛出异常时,可以根据异常类型决定跳转到哪个页面。 10. **国际化(Internationalization, i18n)**:Struts 2支持多语言,通过资源...
7. **结果类型(Result Type)**:Struts2支持多种结果类型,如`dispatcher`(默认,用于转发到JSP),`redirect`(用于重定向到另一个URL)。结果类型定义了Action执行后如何跳转到下一个页面。 总的来说,Struts2...
2. **配置文件**:`struts.xml`是Struts2的核心配置文件,用于定义Action、Result和拦截器链。 3. **Action类**:Action类是业务逻辑的载体,处理用户请求并返回结果。 4. **结果类型**:结果类型定义了Action执行后...
4. **结果类型(Result)**: 结果类型决定了Action执行后如何跳转到视图。例如,`dispatcher`结果类型将控制权交给Servlet容器来处理视图,而`redirect`则会发起一个新的HTTP请求。在`...
Result则负责如何处理Action的结果,比如跳转到另一个页面、生成JSON响应等。 7. **依赖注入**:Struts2支持依赖注入(DI),允许Action类通过接口获取服务,而非直接实例化依赖对象。这有助于实现松耦合,提高测试...
Struts 2是Java开发Web应用的一个流行框架,它的核心基于Model-View-Controller(MVC)设计模式。本文将深入探讨Struts 2.0的相关知识点,包括DTD(Document Type Definition)的作用、Struts配置文件`struts.xml`的...
Interceptor是Struts2的一个重要创新,它允许在Action执行前后插入自定义逻辑,如日志、权限检查等。Result则定义了Action执行后的跳转策略,可以是页面、重定向或者其他动作。ValueStack是对象容器,它简化了模型...
- **Result**:Result用于定义Action处理后的返回结果,比如跳转到某个页面或者返回JSON数据等。 - **View**:视图层通常由JSP页面或者其他模板引擎实现,负责展示数据给用户。 #### Struts2配置文件详解 - **...
Action是Struts2的主要业务逻辑载体,它负责处理用户请求,执行业务逻辑,并返回结果到视图层。 12. **Struts2_结果类型** 结果类型定义了Action执行后跳转的目标,可以是另一个Action、JSP页面、静态资源等,...
Struts2是一个强大的Java web应用程序框架,它基于MVC(Model-View-Controller)设计模式,主要用于构建可维护性高、结构清晰的企业级应用。在这个"Struts2项目案例"中,我们可以通过学习和实践来深入理解Struts2...
1. **Action类**:在Struts2中,业务逻辑通常封装在Action类中。Action类是处理用户请求的实体,它执行特定的业务操作,并通过返回一个结果来决定页面的跳转。 2. **配置文件**:Struts2的核心配置文件是struts.xml...
在Struts2中,模型对象通常由Action类管理,它们封装了业务数据,并在视图层进行展示。 6. **依赖注入(Dependency Injection, DI)**:Struts2支持Spring等DI框架,允许在运行时动态地将依赖关系注入到Action类和...
Result定义了如何处理Action执行后的结果,如跳转到新的页面或展示JSON数据。 3. **配置文件**: Struts 2的配置主要在struts.xml文件中进行,这里定义了Action的映射、结果类型、拦截器等。开发者可以使用XML或...
Struts2作为一款经典的Java Web框架,为开发者提供了强大的MVC(Model-View-Controller)支持,极大地简化了Web应用的开发。在“Web开发学习8Struts2基类封装”这一主题中,我们将深入探讨Struts2框架的核心概念、...
- **Result**:结果表示Action执行后的输出,如跳转到新的JSP页面或返回JSON数据。 2. **配置机制**: - **struts.xml**:这是Struts2的主要配置文件,定义了Action、结果类型、拦截器栈等。 - **动态方法调用...
根据提供的信息,我们可以深入探讨《Struts2实战》这本书中所涵盖的关键知识点,尤其是在描述中提到的关于Struts2框架与MVC模式的关系及其在Web开发中的应用。 ### Struts2框架简介 Struts2是Struts框架的一个升级...
- **核心功能**:Struts2框架的核心功能之一就是将业务逻辑与展示层分离,实现MVC(Model-View-Controller)架构模式。这有助于提高代码的可维护性、可扩展性和可测试性。 - **请求处理**:通过定义特定的Action来...
1. **MVC设计模式**:Struts2基于Model-View-Controller(MVC)设计模式,将业务逻辑、数据和用户界面分离,提高代码的可维护性和可重用性。 2. **Action类**:在Struts2中,Action类是处理用户请求的核心组件。一...