++YONG原创,转载请注明
1. Struts2的由来:
Struts 1是全世界第一个发布的MVC框架,它由Craig McClanahan在2001年6月发布,该框架一经推出,就得到了世界上Java Web开发者的拥护,经过长达6年时间的锤炼,Struts 1框架更加成熟、稳定,性能也有了很好的保证。因此,到目前为止,Struts 1依然是世界上使用最广泛的MVC框架。
目前,基于Web的MVC框架非常多,发展也很快,每隔一段时间就有一个新的MVC框架发布,例如像JSF、Tapestry和Spring MVC等。除了这些有名的MVC框架外,还有一些边缘团队的MVC框架也很有借鉴意义。
对于企业实际使用MVC框架而言,框架的稳定性则应该是最值得考虑的问题。一个刚刚起步的框架,可能本身就存在一些隐藏的问题,会将自身的BUG引入自己的应用。
虽然Struts 2号称是一个全新的框架,但这仅仅是相对Struts 1而言。Struts 2与Struts 1相比,确实有很多革命性的改进,但它并不是新发布的新框架,而是在另一个赫赫有名的框架:WebWork基础上发展起来的。从某种程度上来讲,Strut2没有继承Struts 1的血统,而是继承了WebWork的血统。或者说,WebWork衍生出了Struts 2,而不是Struts 1衍生了Struts 2。因为
Struts 2是WebWork的升级,而不是一个全新的框架,因此稳定性、性能等各方面都有很好的保证;而且吸收了Struts 1和WebWork两者的优势,因此,是一个非常值得期待的框架。
1.1. Struts1存在的问题:
1) 支持的表现层技术单一:
Struts 1只支持JSP作为表现层技术,不提供与其他表现层技术,例如Velocity、FreeMarker等技术的整合。这一点严重制约了Struts 1框架的使用
2) 与Servlet API严重耦合,难于测试
3) 代码严重依赖于Struts 1 API,属于侵入式设计
1.2. WebWork简介:
1) webWork的数据流图:
2) 对Struts 1的种种缺点而言,WebWork存在如下优点:
a. Action无需与Servlet API耦合,更容易测试
b. Action无需与WebWork耦合,代码重用率高
c. 支持更多的表现层技术,有更好的适应性:WebWork对多种表现层技术:JSP、Velocity和FreeMarker等都有很好的支持
2. Struts2框架概述:
Struts 2的体系与Struts 1体系的差别非常大,因为Struts 2使用了WebWork的设计核心,而不是使用Struts 1的设计核心。Struts 2大量使用拦截器来处理用户请求,从而允许用户的业务逻辑控制器与Servlet API分离。
Struts 2 是一个雅致的,可扩展的,用来建立企业级Java Web应用程序的框架。
Struts 2 不但注重程序的开发过程,更注重部署和后期维护。
Struts 2 来源于WebWork 2。
Struts 2 融合了Struts 和 WebWork的社区力量,是这两个社区努力的结果。
Struts 2 非常容易使用。
Struts 2 最大可能的保留了和Strut 1.x的相似行。
3. Struts2的安装配置:
3.1. 下载:
进入apache的官方网:
http://struts.apache.org/download.cgi#struts209 下载struts2的GA完整版,当前最新版本是2.0.9。本文以struts2.0.9为例。
下载完后,解压到本地磁盘,该文件夹包含如下文件结构:
l apps:该文件夹下包含了struts 2 的示例应用。
l docs:struts2的相关文档,包含struts2的快速入门、struts2的帮助文档及API文档等内容。
l j4:该文件夹下包含了让struts2支持JDK1.4的JAR文件。
l lib:该文件夹下包含了struts2框架的核心类库,以及struts2的第三方插件类库。
l src:该文件下包含了struts2框架的全部源代码。
3.2. struts2应用的平台要求:
struts2应用默认需要Java 5运行时环境,需要web容器支持Servlet API2.4和JSP API2.0。若使用jdk1.4运行时环境,则使用j4下的包。
3.3. 配置:
将struts2的必需类库:struts2-core-2.0.9.jar、xwork-2.0.4.jar、ognl-2.6.11.jar、freemarker-2.3.8.jar、commons-logging-1.0.4.jar复制到web应用的WEB-INF/lib路径下。当然,如果你的web应用需要使用struts2的更多特性,则需要从lib目录把其它相应Jar包复制到WEB-INF/lib目录下。
4. Struts2第一个示例:
4.1. 实例描述:
假设有一个名为test的用户,其密码是test,程序要完成的任务是,呈现一个登录界面给用户,如果用户输入的名称和密码都正确返回一个欢迎页面给用户,否则,就返回登录页面要求用户重新登录并显示相应的出错信息。
4.2. 创建Web应用:
Eclipse工程视图:
4.3. 配置struts2的核心Filter:
编辑web应用的web.xml配置文件,配置struts2的核心Filter:
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
web-app
version
=
"2.4"
xmlns
=
"http://java.sun.com/xml/ns/j2ee"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
=
"http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<
welcome-file-list
>
<
welcome-file
>
index.jsp
</
welcome-file
>
</
welcome-file-list
>
</
web-app
>
|
Struts2应用中所有的用户请求由Struts2框架的核心控制器FilterDispatcher处理,它默认拦截所有后缀为.action的请求,再由它根据*.action请求的前面部分决定调用哪个业务控制器。
4.4. 实现业务控制器类:
Struts2下的控制器不再像Struts1.x下的控制器,需要继承一个Actiono类,它甚至可以不实现任何接口,struts2的业务控制器就是一个包含execute()方法的普通POJO。该类包含多个属性用于封装用户的请求参数和要呈现给客户端的数据。
/*
* ClassName: LoginAction.java
* Author: qiujy
* CreateTime: Sep 26, 2007
*
* Copyright 2007 ++YONG All rights reserved.
* EMail: qiujiayong@126.com
*/
package
org.qiujy.web.struts2.action;
/**
*
@author
qiujy
*
@version
1.0
*/
publicclass
LoginAction {
private
String
userName
;
private
String
password
;
/**
*
@return
the
userName
*/
public
String getUserName() {
return
userName
;
}
/**
*
@param
userName
the
userName
to
set
*/
publicvoid
setUserName(String userName) {
this
.
userName
= userName;
}
/**
*
@return
the
password
*/
public
String getPassword() {
return
password
;
}
/**
*
@param
password
the
password
to
set
*/
publicvoid
setPassword(String password) {
this
.
password
= password;
}
/**
*
处理用户请求的
excute()
方法
*
@return
结果导航字符串
*
@throws
Exception
*/
public
String execute()
throws
Exception{
if
(
"test"
.equals(
this
.
userName
) &&
"test"
.equals(
this
.
password
)){
return
"success"
;
}
else
{
return
"error"
;
}
}
}
|
4.5. 配置Action:
为了让该Action能处理用户的请求,还需要将该Action配置在struts.xml文件中。struts.xml文件要存放在classes路径下,它主要用来配置Struts2的Action定义及Action处理结果和物理资源之间的映射关系。
<!
DOCTYPE
struts
PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd"
>
<
struts
>
<
include
file
=
"struts-default.xml"
/>
<!-- struts2
的
action
必须放在一个指定的包空间下定义
-->
<
package
name
=
"default"
extends
=
"struts-default"
>
<!--
定义处理请求
URL
为
login.action
的
Action -->
<
action
name
=
"login"
class
=
"org.qiujy.web.struts.action.LoginAction"
>
<!--
定义处理结果字符串和资源之间的映射关系
-->
<
result
name
=
"success"
>
/success.jsp
</
result
>
<
result
name
=
"error"
>
/error.jsp
</
result
>
</
action
>
</
package
>
</
struts
>
|
如上表示的意思是:该Action将负责处理URL为login.action的用户请求。处理时,Action将调用它的execute()方法处理用户请求,如果execute()方法返回success这个结果字符串,请求将被转发到/success.jsp页面,如果execute()方法返回error,则请求被转发到error.jsp页面。
4.6. 创建视图页面:
1. index.jsp:此处两个要提交的表单域的名字必须和Action声明的属性同名。
<%@
page
language
=
"java"
pageEncoding
=
"UTF-8"
%>
<
html
>
<
head
>
<
title
>
用户登录页面
</
title
>
</
head
>
<
body
>
<
h2
>
用户入口
</
h2
>
<
hr
>
<
form
action
=
"login.action"
method
=
"post"
>
<
table
border
=
"1"
>
<
tr
>
<
td
>
用户名
:
</
td
>
<
td
><
input
type
=
"text"
name
=
"userName"
/></
td
>
</
tr
>
<
tr
>
<
td
>
密码
:
</
td
>
<
td
><
input
type
=
"password"
name
=
"password"
/></
td
>
</
tr
>
<
tr
>
<
td
colspan
=
"2"
>
<
input
type
=
"submit"
value
=
"
确定
"
/>
</
td
>
</
tr
>
</
table
>
</
form
>
</
body
>
</
html
>
|
2. success.jsp:这里用到struts2的标签库了
<%@
page
language
=
"java"
pageEncoding
=
"UTF-8"
%>
<%@
taglib
prefix
=
"s"
uri
=
"/struts-tags"
%>
<
html
>
<
head
>
<
title
>
登录成功
</
title
>
</
head
>
<
body
>
<
h2
>
登录成功
</
h2
>
<
br
>
欢迎
<
s:property
value
=
"userName"
/>
!!!
</
body
>
</
html
>
|
3. error.jsp:
<%@
page
language
=
"java"
pageEncoding
=
"UTF-8"
%>
<
html
>
<
head
>
<
title
>
登录失败
</
title
>
</
head
>
<
body
>
<
h2
style
=
"color:red"
>
登录失败
</
h2
>
<
br
>
请重试!!!
</
body
>
</
html
>
|
4.7. 效果图:
4.8. 本例源码:
不知道为什么传不上来???一上传就无响应
5. Struts 2框架的处理流程:
一个请求在Struts2框架中的处理大概分为以下几个步骤
1) 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2) 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3) 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action。
4) 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy。
5) ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类。
6) ActionProxy创建一个ActionInvocation的实例。
7) ActionInvocation实例使用命名模式来调用,回调Action的execute方法,该execute方法先获取用户请求参数,然后它会调用业务逻辑组件来处理用户的请求。在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8) 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper。
在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。
6. Struts 1和Struts 2对比
6.1. 在Action实现类方面的对比:
Struts 1要求Action类继承一个抽象基类;Struts 1的一个具体问题是使用抽象类编程而不是接口。
Struts 2 Action类可以实现一个Action接口,也可以实现其他接口,使可选和定制的服务成为可能。Struts 2提供一个ActionSupport基类去实现常用的接口。即使Action接口不是必须实现的,只有一个包含execute方法的POJO类都可以用作Struts 2的Action。
6.2. 线程模式方面的对比:
Struts 1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts 1 Action能做的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的;
Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。
6.3. Servlet依赖方面的对比:
Struts 1 Action依赖于Servlet API,因为Struts 1 Action的execute方法中有HttpServletRequest和HttpServletResponse方法。
Struts 2 Action不再依赖于Servlet API,从而允许Action脱离Web容器运行,从而降低了测试Action的难度。 当然,如果Action需要直接访问HttpServletRequest和HttpServletResponse参数,Struts 2 Action仍然可以访问它们。但是,大部分时候,Action都无需直接访问HttpServetRequest和HttpServletResponse,从而给开发者更多灵活的选择。
6.4. 可测性方面的对比:
测试Struts 1 Action的一个主要问题是execute方法依赖于Servlet API,这使得Action的测试要依赖于Web容器。为了脱离Web容器测试Struts 1的Action,必须借助于第三方扩展:Struts TestCase,该扩展下包含了系列的Mock对象(模拟了HttpServetRequest和HttpServletResponse对象),从而可以脱离Web容器测试Struts 1的Action类。
Struts 2 Action可以通过初始化、设置属性、调用方法来测试。
6.5. 封装请求参数的对比:
Struts 1使用ActionForm对象封装用户的请求参数,所有的ActionForm必须继承一个基类:ActionForm。普通的JavaBean不能用作ActionForm,因此,开发者必须创建大量的ActionForm类封装用户请求参数。虽然Struts 1提供了动态ActionForm来简化ActionForm的开发,但依然需要在配置文件中定义ActionForm;
Struts 2直接使用Action属性来封装用户请求属性,避免了开发者需要大量开发ActionForm类的烦琐,实际上,这些属性还可以是包含子属性的Rich对象类型。如果开发者依然怀念Struts 1 ActionForm的模式,Struts 2提供了ModelDriven模式,可以让开发者使用单独的Model对象来封装用户请求参数,但该Model对象无需继承任何Struts 2基类,是一个POJO,从而降低了代码污染。
6.6. 表达式语言方面的对比:
Struts 1整合了JSTL,因此可以使用JSTL表达式语言。这种表达式语言有基本对象图遍历,但在对集合和索引属性的支持上则功能不强;
Struts 2可以使用JSTL,但它整合了一种更强大和灵活的表达式语言:OGNL(Object Graph Notation Language),因此,Struts 2下的表达式语言功能更加强大。
6.7. 绑定值到视图的对比:
Struts 1使用标准JSP机制把对象绑定到视图页面;
Struts 2使用“ValueStack”技术,使标签库能够访问值,而不需要把对象和视图页面绑定在一起。
6.8. 类型转换的对比:
Struts 1 ActionForm 属性通常都是String类型。Struts 1使用Commons-Beanutils进行类型转换,每个类一个转换器,转换器是不可配置的;
Struts 2使用OGNL进行类型转换,支持基本数据类型和常用对象之间的转换。
6.9. 数据校验的对比:
Struts 1支持在ActionForm重写validate方法中手动校验,或者通过整合Commons alidator框架来完成数据校验。
Struts 2支持通过重写validate方法进行校验,也支持整合XWork校验框架进行校验。
6.10. Action执行控制的对比:
Struts 1支持每一个模块对应一个请求处理(即生命周期的概念),但是模块中的所有Action必须共享相同的生命周期。
Struts 2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。开发者可以根据需要创建相应堆栈,从而和不同的Action一起使用。
7. 本系列文章的参考资料:
1.《STRUTS 2权威指南》作者:李刚
2. Blog――Max On Java :http://www.blogjava.net/max/category/16130.html
3. Blog――Struts 2的专栏 :http://blog.csdn.net/struts2/archive/2007/08/01/1721752.aspx