小弟最近正在学习Struts2,由SpringSide的文档里面发现infoq上有这个系列的文章,觉得不错,但是无奈英文太差。所以就把我对这个文章的理解写了出来。红色标出来的地方都是我没什么把握的,或者压根就看不明白的,希望各位老大能够指点一下,多谢多谢了!
Migrating Struts Apps to Struts 2
Introduction / History
The first version of Struts was released in June 2001.
It was born out of the idea that JSPs and servlets could
be used together to provide a clean separation between
the view and the business or application logic of a
web application. Before Struts, the most common options
were to add business and application logic to the JSP,
or to render the view from servlets using println()
statements.
Since its release, Struts has become the de-facto standard
for web application. With this popularity have come
enhancements and changes - both to keep up with the
ever-changing requirements for a web application framework,
but also to match features with the ever-increasing
number of competing frameworks available.
To that end, there have been several proposals for
the next generation of Struts. The two alternatives
that have become the most cohesive in the past year
are Shale and Struts Ti. Shale is a component based
framework, and just recently has become its own top-level
Apache project, where Struts Ti continues the front
controller or model-view-controller pattern that has
made Struts so successful.
The WebWork project was started as a Struts revolution
- as a fork of the Struts code to introduce new ideas,
concepts and functionality that may not be compatible
with the original Struts code - and it was first released
in March 2002. WebWork is a mature framework, having
undergone several minor and major releases.
In December 2005 it was announced that WebWork and
the Struts Ti would join forces. Since that time, Struts
Ti has become Struts Action Framework 2.0, and the successor
to Struts.
Finally, it should be noted that neither the Struts
nor WebWork projects are going away. While interest
is high, and willing developers are available, both
of these projects will continue - all the while having
bugs fixed, and adding enhancements and new features.
这一段就不翻译了,都是些陈芝麻烂谷子,没什么太大用。
A Request Walk-through
Before we start looking at the low level details of
how to convert an application from Struts to Struts2,
let's take a look at what the new architecture looks
like by walking through the request processing.
As we walk through the request lifecycle you should
notice one important fact - Struts2 is still a front
controller framework. All of the concepts that you are
familiar with will still apply.
在我们开始关注将一个Struts应用转换为struts2应用的细节问题之间,让我们先走一遍请求处理的流程,来了解一下struts2的新架构
在我们走一遍请求的生命周期之后,你会注意到一个重要的事实--struts2仍然是一个front
controller framework(前端控制框架?)。你所熟悉的相关概念仍然适用(这里应该指的是sturts当中的一些关于请求处理的概念)
This means:
- Actions will still be invoked via URL's Data is
still sent to the server via the URL request parameters
and form parameters
- All those Servlet objects (request,
response, session, etc.) are all still available to
the Action
这意味着:
1.仍然可以使用URL来调用Action,可以通过URL请求参数或者form的参数向服务器发送数据;
2.如request,response,session等Servlet对象仍然可以再Action当中使用
From a high-level overview, this is how the request
is processed:
在一个相对比较高的级别上来看,请求的处理过程如下图:
The processing of a request can be broken into these
6 steps:
- A request is made and processed by
the framework - the framework matches
the request to a configuration so that the interceptors,
action class and results to use are known.
- The request passes through a series
of interceptors - interceptors, and
interceptor stacks, can be configured at a number
of different levels for the request. They provide
pre-processing for the request as well as cross-cutting
application features. This is similar to the Struts
RequestProcessor class which uses the Jakarta Commons
Chain component.
- The Action is invoked
- a new instance of the action class is created and
the method that is providing the logic for this request
is invoked. We will discuss this in more detail in
the second part of this series; however, in Struts2
the configuration of the action can specify the method
of the action class to be invoked for this request.
- The Result is invoked
- the result class that matches the return from processing
the actions' method is obtained, a new instance created
and invoked. One possible outcome of the result being
processed is rendering of a UI template (but not the
only one) to produce HTML. If this is the case, then
Struts2 tags in the template can reach back into the
action to obtain values to be rendered.
- The request returns through the Interceptors
- the request passes back through the interceptors
in reverse order, allowing any clean-up or additional
processing to be performed.
- The response is returned to the user
- the last step is to return control back to the servlet
engine. The most common outcome is that HTML is rendered
to the user, but it may also be that specific HTTP
headers are returned or a HTTP redirect is invoked.
请求除了的过程可以分为6步:
1.框架负责创建和处理request--the
framework matches the request to a configuration so
that the interceptors, action class and results to use
are known.
2.request要通过一系列的拦截器--可以在request的多个不同级别上配置拦截器,它们可以提供对request对象的前处理(所谓的前处理我觉得就是在正式的处理request之前的一些准备工作,如从request当中取出传递的参数)
as well as cross-cutting
application features.这就像是Struts当中的RequestProcessor,使用了Jakarta
Commons Chain组件(一种实现责任链模式的组件)
3.Action是被调用。Action类的对象被创建,Action当中的函数(为request提供逻辑运算)被调用。我们会在本系列文章的第二部分详细的讨论这个部分。在Struts2当中,可以配置接收到request之后是调用哪个Action当中的哪个函数来处理;
4.Result被调用。the
result class that matches the return from processing
the actions' method is obtained, a new instance created
and invoked.Result被处理的一个可能的结果是渲染UI模板(但是不仅仅是一个)来产生HTML。在这样的一个案例当中(也就是说result被处理的结果是渲染模板这种情况),在模板当中的Struts2标签可以回顾Action,来获得渲染模板所需要的数据(回顾是字典上对于reach
back的解释,我的理解就是说Struts2的标签可以访问Action对象当中的值)
5.request返回通过拦截器--request以一个相反的顺序(相对于request第一通过拦截器的顺序)通过拦截器,这样一些清理工作,或者是其他额外的处理都可以在这个时候被执行了
As you may have noticed, there are some differences.
The most obvious one is that Struts2 is a pull-MVC architecture.
What does this mean? From a developers perspective it
means that data that needs to be displayed to the user
can be pulled from the Action. This differs from Struts,
where data is expected to be present in beans in either
the HTTP page, request or session scopes. There are
other places that data can be pulled from, and we'll
talk about those as the scenarios come up.
你可能已经注意到了,这里有一些差别。一个明显的差别是Struts2是一个pull(推)MVC架构。这是什么意思呢。从一个开发者的角度来看,这意味着是由Action
pull(推)出了需要显示给客户的数据。而在Struts当中,数据被封装在bean当中,然后被放在page,request或session当中。Action还可以将数据pull到别的地方,and
we'll talk about those as the scenarios come up.
Configuring the framework
配置框架
The first, and most important configuration, is the
one that enables the web application framework within
the servlet containers web.xml file.
The configuration that everyone should be familiar
with for Struts is:
首先最重要的配置是让web应用程序框架能够在Servlet容器当中运行,需要通过修改web.xml来实现.
对Struts的配置大体上如下:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
For Struts2 there are very few changes. The most significant
is that the dispatcher has been changed from a servlet
to a servlet filter. The configuration is just as easy
as for a servlet, and shown here:
在Struts2当中有一些变化。最重大的改变是转发器(dispatcheer)从servlet变成了filter。配置的方法和Servlet一样简单:
<filter>
<filter-name>webwork</filter-name>
<filter-class>
org.apache.struts.action2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>webwork</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Similar to the servlet configuration, the filter configuration
defines a name (for reference) and the class of the
filter. A filter mapping links the name with the URI
pattern that will invoke the filter. By default, the
extension is ".action". This is defined in
the default.properties file (within the Struts2 JAR
file) as the "struts.action.extension" property.
SIDEBAR: The "default.properties"
file is the place that many configuration options
are defined. By including a file with the name "struts.properties"
in the classpath of your web application, with different
values for the properties, you can override the default
configuration.
就像Servlet的配置一样,filter的配置需要定义一个name(为了以后可以引用的到)和fileter的class。filter
mapping连接了name和URI pattern,当通过此URI pattern访问应用程序的时候,filter会被调用。默认的,扩展名是".action"。这个配置是用default.properties(在Struts2的JAR文件当中)的
"struts.action.extension" 属性来定义的。
工具条:在"default.properties"文件当中,有很多配置选项被定义。通过在你的web应用当中包含一个名为"struts.properties",然后修改其中的属性,你就可以重写默认配置
For Struts, the servlet configuration provides an init-param
tag that defines the names of the files used to configure
Struts. Struts2 does not have such a configuration parameter.
Instead, the default configuration file for Struts2
has the name "struts.xml" and needs to be
on the classpath of the web application.
SIDEBAR/TIP: Since there is
a namespace separation between the Struts actions
(with a ".do" extension) and the Struts2
actions (with a ".action") extension, there
is no reason why they cannot co-exist within the same
web application. This is a great way to start the
migration process - add the necessary configuration,
and start developing all new functionality in Struts2.
As time and resources permit, the remaining actions
can be converted. Or, the two frameworks can co-exist
peacefully forever, since there is no reason that
the existing action ever needs to be migrated. Another
migration strategy is to update only the actions by
changing the Struts2 extension to ".do".
This allows the existing JSP's to remain the same
and be re-used.
在Struts当中,Servlet的配置需要一个init-param标签来定义struts的配置文件。Struts2不需要这个配置参数。Struts2的默认配置是需要一个名为“struts.xml”的文件放在web应用程序的classpath当中
工具条:在Struts当当中命名空间的分隔符是".do",而在Struts2当中是".action",所以这两个框架可以在一个web当中共存。这是对于迁移过程来讲是个好的开始--添加必要的配置,然后用Struts2开发所有新的功能。当时间和资源允许时,再对原来遗留的action进行修改。如果已经存在的action不需要迁移,两个框架可以一直和平共处下去。另外一个迁移策略是将Struts2的扩展名改为.do,这要就可以重用已经存在的JSP。
Deconstructing the Actions
分析Action的结构
In the request walk-through we spoke about some of
the differences between Struts and Struts2 from a high
level. Let's take it a step deeper now, and look at
the differences between the structures of the actions
in each framework.
Let's first review the general structure of the Struts
action. The general form of the Struts action looks
like this:
在request walk-through当中,我们讲了一些Struts和Struts在较高层面上的区别,现在让我们深入一些,来看看在这两个框在在Action上面的区别。
让我们首先回顾一下Struts的action的结构。一个Struts的Action应该是如下的这个样子:
public class MyAction extends Action {
public ActionForward execute(ActionMapping
mapping,
ActionForm
form,
HttpServletRequest
request,
HttpServletResponse
response)
throws
Exception {
// do
the work
return
(mapping.findForward("success"));
}
}
When implementing a Struts action, you need to be aware
of the following items:
- All actions have to extend the Action base class.
- All actions have to be thread-safe, as only a single
action instance is created.
- Because the actions have to be thread-safe, all
the objects that may be needed in the processing of
the action are passed in the method signature.
- The name of the method that is invoked for the processing
of the action is "execute" (there is a DispatchAction
class available in Struts which can re-route the method
to be executed to another method in the same action,
however the initial entry point from the framework
into the action is still the "execute" method).
- An ActionForward result is returned using a method
from the ActionMapping class, most commonly via the
"findForward" method call
实现一个Struts的Action,你需要知道如下的几点:
1.所有的Action必须实现Action基类
2.因为只会为每个Action创建一个对象,所以所有的Action都必须是线程安全的。
3.因为Action必须是线程安全的,所以所有action需要处理的对象都必须通过方法参数传递到Action当中;
4.Action当中被调用用来处理请求的函数叫做"execute"(在Struts当中,有一个名为DispatchAction的action,它可以根据接受到得请求来决定调用该action的哪个函数,但是实际上框架当中的最初入口点还是execute函数)
5.通常,通过调用ActionMapping的''findForward”函数来得到一个ActionForward对象,作为Action当中execute的返回值
In contrast, the Struts2 action provides a much simpler
implementation. Here's what it looks like:
相比之下,Struts2的Action提供了一个更加简单的实现:
public class MyAction {
public String execute() throws Exception
{
// do
the work
return
"success";
}
}
The first thing you may have noticed is that the action
doesn't extend any classes or interfaces. In fact, it
goes further than this. By convention, the method invoked
in the processing of an action is the "execute"
method - but it doesn't have to be. Any method that
follows the method signature public String methodName()
can be invoked through configuration.
Next, the return object is a String. If you don't like
the idea of string literals in your code, there is a
helper interface Action available that provides the
common results of "success", "none",
"error", "input" and "login"
as constants.
Finally, and perhaps the most revolutionary difference
from the original Struts implementation, is that the
method invoked in the processing of an action (the "execute"
method) has no parameter. So how do you get access to
the objects that you need to work with? The answer lies
in the "inversion of control" or "dependency
injection" pattern (for more information Martin
Fowler has an informative article at
http://www.martinfowler.com/articles/injection.html).
The Spring Framework has popularized this pattern, however,
the predecessor to Struts2 (WebWork) started using the
pattern around the same time.
To understand the inversion of control better, let's
look at an example where the processing of the action
requires access to the current requests HttpServerRequest
object.
你可能首先主要到这个action并没有继承任何的类或者借口。实际上,不仅仅如此。习惯上,action处理请求的函数名字叫做“execute”,但是并不一定要这样做。任何方法只要签名是public
String methodName(),都可以通过配置被调用;
其次,返回的对象是String。如果你不喜欢在你的代码当中使用String字面量(不知道理解的对不对),有一个可以在Action当中使用的助手接口,提供了一些公用的result,如"success",
"none", "error", "input"
and "login" 作为常量。
最后,最具革命性的变化是在Struts2当中处理请求的函数("execute"函数)是没有参数的。那你怎么访问你所需要的对象呢?答案是“反转控制”或“依赖注入”模式(关于这两个模式的详细信息你可一个在Martin
Fowler 所写的文章中找到http://www.martinfowler.com/articles/injection.html)。Spring框架推广了这个模式,然而Struts2的前身(WebWork)几乎是在相同的时间也使用了这个模式。
让我们通过一个在Action当中访问HttpServletRequest(这个地方应该是原文写错了吧)对象的例子来更好的理解反转控制:
The dependency injection mechanism used in this example
is interface injection. As the name implies, with interface
injection there is an interface that needs to be implemented.
This interface contains setters, which in turn are used
to provide data to the action. In our example we are
using the ServletRequestAware interface, here it is:
在例子当中,我们使用依赖注入机制的接口注入。顾名思义,使用接口注入必须实现一个借口。接口中包含setter,负责为Action提供数据。在我们的例子当中使用ServletRequestAware接口:
public interface ServletRequestAware {
public void setServletRequest(HttpServletRequest
request);
}
When we implement this interface, our simple action
from above becomes a little more complex - but now we
have a HttpServerRequest object to use.
当我们实现了这个接口,我们的action可能会变得有一点复杂-但是现在我们可以使用httpServletRequest对象了:
public class MyAction implements ServletRequestAware
{
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest
request) {
this.request
= request;
}
public String execute() throws Exception
{
// do
the work using the request
return
Action.SUCCESS;
}
}
At this point, some alarm bells are probably going
off. There are now class level attributes in the action
- which, although not thread-safe, is actually okay.
In Struts2, an action instance is created for each request.
It's not shared and it's discarded after the request
has been completed.
There is one last step left, and that is to associate
the ServletConfigInterceptor interceptor
with this action. This interceptor provides the functionality
to obtain the HttpServletRequest and inject it into
actions that implement the ServletRequestAware
interface. For the moment, don't worry about the details
of the configuration - we'll go into much more detail
in the next article. The important thing at the moment
is to understand that the interceptor and the interface
work hand-in-hand to provide the dependency injection
to the action.
The benefit to this design is that the action is completely
de-coupled from the framework. The action becomes a
simple POJO that can also be used outside of the framework.
And for those that encourage unit testing, testing a
Struts2 action is going to be significantly easier than
wrestling to get a Struts action into a StrutsTestCase
or a MockStrutsTestCase unit test.
这个时候,警铃已经拉响了。在Action当中有成员变量,是非线程安全的,居然也可以。因为在Struts2当中,会为每个request创建一个Action对象。所以action对象不会在被共享,当请求结束之后,它就会被丢弃掉。
那么最后一个步骤就是将ServletConfigInterceptor拦截器关联到这个action。这个过滤器提供获得HttpServletReques对象t的方法,并将它注入到实现ServletRequestAware接口的Action当中。目前,不用担心配置的细节,我们会在下篇文章中详细介绍。现在的重点是要理解拦截器和接口共同工作为Action提供依赖注入。
这种设计的好处是减弱action对于框架的依赖。action变成了一个简单的POJO,可以再框架之外使用。而其可以鼓励单元测试。Struts2的单元测试相对Struts要简单的多。因为你不需要和StrutsTestCase和MockStrutsTestCase摔跤(我的理解就是StrutsTestCase和MockStrutsTestCase用起来非常的麻烦)
Conclusion / Wrap-Up
总结/预报
By now you should be familiar with the basics of Struts2
- the high level architecture and basic request workflow.
You should be able to configure Struts2 in a servlet
container, and know the differences between the action
for Struts and Struts2.
In the next article we will introduce the example application
that will be migrated, as well as building on the knowledge
attained here to migrate the example applications' actions
from Struts to Struts2. What we'll end up with is a
working application using JSTL, JSP and Struts2. We'll
also further investigate the differences between actions;
configuration of actions and other framework elements;
and talk more about the action related framework features.
现在你应该在整体架构和基本的request工作流畅上熟悉了Struts2.你应该能够将Struts2配置安装到一个Servlet容器上,也应该明白了Struts和Struts2在action方面的差别。
下篇文章我们会介绍需要迁移的样例应用程序,也会通过将应用程序从Struts迁移到Struts2来构建知识。我们也会更加深入的调查Action之间的区别;action以及框架其他元素的配置方法;了解更多的action框架的相关特性(这句话翻译的好像不对),最终我们会得到一个使用JSTL,JSP和Struts2实现的可以运行的应用程序
|