您的位置:首页 > 编程语言 > Java开发

Struts2相关知识整合

2014-01-14 16:18 459 查看
网站

Apache Struts 2 http://struts.apache.org/2.x/index.html
Opensymphony XWork http://www.opensymphony.com/xwork
Opensymphony WebWork http://www.opensymphony.com/webwork
Struts2是使用Filter作为控制器

Struts2AndStruts1

Struts2和Struts1对比

Struts2,

Struts

Struts 2 = WebWork 2.2

对比:

1.配置文件:Struts1配置文件放在WEB-INF/struts-config.xml(可定制)目录下;Struts2的配置文件要放在WEB-INF/classes目录下

2.控制器:Struts1的控制器是一个ActionServlet类;Struts2的控制器是一个过滤器。

3.动作表单:Struts1的HTML表单对应一个ActionForm类的实例,动作类可访问对应配置的ActionForm,操作ActionForm进行填充数据传输对象;Struts2的HTML表单直接映射成POJO,动作类中可直接访问POJO,操作对POJO的验证。

4.动作类:Struts1的动作类继承org.apache.struts.action.Action类;Struts2的动作类可以是任何一个POJO,但是最好是继承ActionSupport类?。

5.显示对象:在JSP中Struts2使用OGNL来显示各种对象模型,JSP自带的JSTL和EL中EL常用来补充使用。

6.标签库:Struts1常用HTML标签库、Bean标签库和Logic标签库;Struts2有通用标签库、表单标签库? 。

为什么最好是继承ActionSupport类 ?Struts2的标签具体有哪些

Action 跟 Actionsupport 的区别

当我们在写action的时候,可以实现Action接口,也可以继承Actionsupport这个类.到底这两个有什么区别呢?

Action接口有:

public static final java.lang.String SUCCESS = "success";

public static final java.lang.String NONE = "none";

public static final java.lang.String ERROR = "error";

public static final java.lang.String INPUT = "input";

public static final java.lang.String LOGIN = "login";

public abstract java.lang.String execute() throws java.lang.Exception;

而Actionsupport这个工具类在实现了Action接口的基础上还定义了一个validate()方法,重写该方法,它会在execute()方法之前执行,如校验失败,会转入input处,必须在配置该Action时配置input属性。

另外,Actionsupport还提供了一个getText(String key)方法还实现国际化,该方法从资源文件上获取国际化信息.

这样在自定义标签时可以定义一个变量为new actionsupport对象实现国际化。

ActionSupport类的作用

struts2不要求我们自己设计的action类继承任何的struts基类或struts接口,但是我们为了方便实现我们自己的action,大多数情况下都会继承com.opensymphony.xwork2.ActionSupport类,并重写此类里的public String execute() throws Exception方法。因为此类中实现了很多的实用借口,提供了很多默认方法,这些默认方法包括国际化信息的方法、默认的处理用户请求的方法等,这样可以大大的简化Acion的开发。

Struts2中通常直接使用Action来封装HTTP请求参数,因此,Action类里还应该包含与请求参数对应的属性,并且为属性提供对应的getter和setter方法。

2.8 增加数据校验

在上面应用中,即使浏览者输入任何用户名、密码,系统也会处理用户请求。在我们整个HelloWorld应用中,这种空用户名、空密码的情况不会引起太大的问题。但如果数据需要保存到数据库,或者需要根据用户输入的用户名、密码查询数据,这些空输入可能引起异常。

为了避免用户的输入引起底层异常,通常我们会在进行业务逻辑操作之前,先执行基本的数据校验。

2.8.1 继承ActionSupport

ActionSupport类是一个工具类,它已经实现了Action接口。除此之外,它还实现了Validateable接口,提供了数据校验功能。通过继承该ActionSupport类,可以简化Struts 2的Action开发。

在Validatable接口中定义了一个validate()方法,重写该方法,如果校验表单输入域出现错误,则将错误添加到ActionSupport类的fieldErrors域中,然后通过OGNL表达式负责输出。

为了让Struts 2增加输入数据校验的功能,改写程序中的LoginAction,增加重写validate方法。修改后的LoginAction类代码如下:

//Struts 2的Action类就是一个普通的Java类
public class LoginAction
{
//下面是Action内用于封装用户请求参数的两个属性
private String username;
private String password;
//username属性对应的getter方法
public String getUsername()
{
return username;
}
//username属性对应的setter方法
public void setUsername(String username)
{
this.username = username;
}
//password属性对应的getter方法
public String getPassword()
{
return password;
}
//password属性对应的setter方法
public void setPassword(String password)
{
this.password = password;
}
//处理用户请求的execute方法
public String execute() throws Exception
{
//当用户请求参数的username等于scott,密码请求参数为tiger时,返回success
字符串
//否则返回error的字符串
if (getUsername().equals("scott")
&& getPassword().equals("tiger") )
{
return "success";
}
else
{
return "error";
}
}
//完成输入校验需要重写的validate方法
public void validate()
{
//如果用户名为空,或者用户名为空字符串
if (getUsername() == null || getUsername().trim().equals(""))
{
//添加表单校验错误
addFieldError("username", "user.required");
}
//当密码为空,或者密码为空字符串时,添加表单校验错误
if (getPassword() == null || getPassword().trim().equals(""))
{
addFieldError("password", "pass.required");
}
}
}

上面的Action类重写了validate方法,该方法会在执行系统的execute方法之前执行,如果执行该方法之后,Action类的fieldErrors中已经包含了数据校验错误,请求将被转发到input逻辑视图处。

为了在校验失败后,系统能将视图转入input处,必须在配置该Action时配置input属性。下面是修改后login Action的配置片段:

<!-- 定义login的Action -->
<action name="Login" class="lee.LoginAction">
<!-- 定义input的逻辑视图名,对应login.jsp页面 -->
<result name="input">/login.jsp</result>
<!-- 定义error的逻辑视图名,对应error.jsp页面 -->
<result name=" success ">/error.jsp</result>
<!-- 定义welcome的逻辑视图名,对应welcome.jsp页面 -->
<result name="success">/welcome.jsp</result>
</action>

对比上面的Action配置与前面的Action配置,我们发现该Action配置片段中增加了input逻辑视图的配置,该逻辑视图映射到login.jsp页面。

前面已经提到:当用户提交请求时,请求得到execute方法处理之前,先会被validate方法处理,如果该方法处理结束后,Action的fieldErrors里的校验错误不为空,请求将被转发给input逻辑视图。如果我们不输入用户名、密码而直接提交表单,将看到如图2.13所示的界面。



图2.13 输入校验的界面
看到这里也许读者觉得非常神奇:我们仅仅在Action添加了数据校验错误,并未在输入页面输出这些校验错误信息,但图2.13所示的页面,却可以看到页面已经输出了这些校验信息——这是因为Struts 2的标签,上面的JSP页面中表单使用的并不是HTML表单,而是用了<s:form .../>标签,Struts 2的<s:form ... />标签已经具备了输出校验错误的能力。

提示 Struts 2的<s:form .../>默认已经提供了输出校验错误的能力。

但上面的程序还存在一个问题:校验信息的国际化。查看上面的Action类代码发现:重写validate方法时,如果发生校验失败的问题,校验错误的提示信息是以硬编码方式写死了——这就失去了国际化的能力。

实际上,ActionSupport类已经提供了国际化信息的能力,它提供了一个getText(String key)方法,该方法用于从资源文件中获取国际化信息。为了让校验信息支持国际化,再次改写Action里的validate方法,改写后的validate方法代码如下:

//执行数据校验的validate方法
public void validate()
{
//如果用户名为空,或者为空字符串
if (getUsername() == null || getUsername().trim().equals(""))
{
//添加校验错误提示,使用getText方法来使提示信息国际化
addFieldError("username", getText("user.required"));
}
if (getPassword() == null || getPassword().trim().equals(""))
{
addFieldError("password", getText("pass.required"));
}
}

在上面的validate方法中,添加校验错误提示时,并不是直接给出了错误提示的字符串,而是调用了getText方法来获取错误提示。因为在Action中,使用getText方法来获取了两个国际化提示:user.required和pass.required,因此应该在国际化资源文件中添加这两条提示信息。

提示 ActionSupport增加了让提示信息国际化的能力,ActionSupport提供的getText方法可以根据资源文件加载获得国际化提示信息。

此时,如果没有任何输出,直接提交登录表单,将看到如图2.14所示的界面。





图2.14 国际化数据校验的错误提示
Struts2Request
Struts2在Action 中访问request,session,application

只需用到attribute

//获取request
ActionContext ctx = ActionContext.getContext();
ctx.put("request", "request");
//获取Session
Map<String,Object> session = ctx.getSession();
session.put("session", "session");
//获取ServletContext
Map<String,Object> appliction = ctx.getApplication();
appliction.put("appliction", "appliction");

获得HttpServletRequest

代码获得

HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = request.getSession();
//不要在Action构造器里调用,可能还没初始化好
ServletContext servletContext = ServletActionContext.getServletContext();

接口获得

如果有多个Action需要用到request,可以写个Action继承接口,使用request的Action继承再它.

/*继承ServletRequestAware(获得request),ServletResponseAware(获得response),
ServletContextAware(获得ServletContext),SessionAware(获得Session)接口*/
public class FormAction implements ServletRequestAware,ServletResponseAware{

private HttpServletRequest request;
private HttpServletResponse response;

//拦截器会把request注入到变量中
public void setServletRequest(HttpServletRequest req) {
this.request = req;
}

public void setServletResponse(HttpServletResponse res) {
this.response = res;
}
}


Struts2ActionMethod
Struts2Action中方法的访问
Updated
May 10, 2010 by
cm2...@gmail.com

默认方法

//Struts2中默认访问的是Action中execute方法
//url:http://127.0.0.1:8080/Struts2Pro/hello.action(访问hello指定的Action中的execute方法)
public String execute() {
return "success";
}

访问Action中多个方法

动态方法调用

只要在url中指定方法名称就可以了. 如:要访问HelloAction中的add()方法 url:http://127.0.0.1:8080/Struts2Pro/hello!add.action (加!和方法名字)

<!-- 在struts.xml中配置可以关闭动态方法(默认是激活,可能会让用户方法没有公开的方法,最好不用) -->
<constant name="struts.enable.DynamicMethodInvocation" value="false" />

配置文件中多个方法

配置多个方法

<!-- url:http://127.0.0.1:8080/Struts2Pro/userActionAdd.action 访问FormAction中的add方法 -->
<action name="userActionAdd" class="com.struts2.form.FormAction" method="add">
<!-- url:http://127.0.0.1:8080/Struts2Pro/userActionUpdate.action 访问FormAction中的update方法 -->
<action name="userActionUpdate" class="com.struts2.form.FormAction" method="update">

使用通配符调用多个方法

<!--
name="*_*" class="com.struts2.form.{1}Action" method="{2}"
url:user_list.action 就是调用userAction中list方法.

name="*_*_*"
url:user_list_abd.action
method="{1}"->user(第一个*对应的字符串)
method="{2}"->list(第二个*对应的字符串)
method="{3}"->abd(第三个*对应的字符串)
-->
<action name="userAction_*" class="com.struts2.form.FormAction" method="{1}">


Struts2OGNL
Struts2_OGNL表达式
Updated
May 10, 2010 by
cm2...@gmail.com

Struts2标签不支持el表达式,只能使用OGNL.

1.OGNL分为Object Stack和Context Map.
把动作和相关对象压入Object Stack.

把各种映射关系(一些Map类型的对象)压入Context Map.(parameters,request,session,application,attr)

OGNL表达式加上一个前缀"#",访问Context Map.
没加访问Object Stack.

2.OGNL访问数组
//String[] colors = {"blue","green","red"};
colors[0]//访问第一个元素
colors.length//访问长度

3.OGNL访问List
countries[0]//访问第一个元素
countries.size//访问countries.size()
countries.isEmpty//访问countries.isEmpty()
创建List
{"a","b","c"}//创建一个由3个String构成的List

4.OGNL访问Map
cities["CA"]或者cities['CA']//访问cities中key为CA的元素
cities.size//访问cities.size()
cities.isEmpty//访问cities.isEmpty()
创建Map
{"CA":"S","WA":"O","UT":"ST"}

%{}里面的表达式会被求值(%{#request.user.age>40}->true or false,%{1+6}->7)


Struts2ResultType
Struts2返回类型
Updated
May 10, 2010 by
cm2...@gmail.com

类型对应的类

Chain(chain)                    构成一条动作链
Dispatcher(dispatcher)          默认类型,转发
FreeMarker(freemarker)          用于与FreeMarker的集成
HttpHeader(httpheader)          把HTTP标头发送回用户
Redirect(redurect)              重定向到另一个URL
RedirectAction(redirectAction)  重定向到另一个Action
Stream(stream)                  把一个InputStream流发送给浏览器(下载用)
Velocity(Velocity)              用于与Velocity技术的集成
XSLT(xslt)                      用于与XML/XSLT技术的集成
PlainText(plaintext)            发送普通文本,通常用来显示JSP页面的源代码

Chain

<!--
Chain用途是构成一条动作链:前一个动作把控制权转交给后一个动作,而前一个动作的状态在后一个动作里仍保持着.
动作链能不用就不用, 有可能把一套连续动作弄成一团乱.
-->
<package name="package1" extends="struts-default">
<action name="action1" class="...">
<result type="chain">action2</result>
</action>

<action name="action2" class="...">
<result type="chain">
<param name="actionName">action3</param>
<param name="namespace">/namespace2</param>
</result>
</action>
</package>

<package name="package2" namespace="/namespace2" extends="struts-default">
<action name="action3" class="...">
<result>/view.jsp</result>
</action>
</package>

Dispatcher

<!-- 转发到JSP,result默认类型 -->
<result name="...">/view.jsp</result>

<!--或者-->
<result name="...">
<param name="location">/view.jsp</param>
</result>

HttpHeader

<!-- 把一个HTTP状态发送给浏览器 -->
<action name="CatchAll">
<result type="httpheader">
<param name="status">404</param>
</result>
</action>

Redirect

<!--
重定向
参数:location:重定向的目的地
parse:   表明是否把location参数的值视为一个OGNL表达式来解释,默认值为true
-->
<action name="..." class="...">
<result name="success" type="redirect">
<!-- 内部资源 -->
/jsp/Product.jsp
<!-- Action带动态参数(${userName}值为本Action中的userName属性值) -->
UserAction.action?userName=${userName}
<!--
外部资源(如果需要使用&和+之类的特殊字符必须使用转义序列.如:&改成&) http://www.google.com?user=1&site=4 转成:http://www.google.com?user=1&site=4
--> http://www.google.com </result>
</action>

RedirectAction

<!--
重定向到一个Action
参数:actionName:指定重定向Action的名字
namespace: 指定重定向Action的命名空间(没有此参数,与本action同一个命名空间)
-->
<result type="redirectAction">UserAction</result>
<!-- 或者 -->
<result type="redirectAction">
<param name="actionName">UserAction</param>
<!-- 参数 -->
<param name="userId">xyz</param>
<param name="area">ga</param>
<result>
<!-- 生成URL:UserAction.action?userId=xyz&area=ga -->

PlainText

<!-- 通常被用来发送JSP页面的源代码 -->
<action name="source_show" class="...">
<result name="success" type="plaintext">/jsp/Menu.jsp</result>
</action>


Struts2Staglib
Struts2的S标签
Updated
May 10, 2010 by
cm2...@gmail.com

<!-- 访问request必须要加# -->
<p>赋值,取值</p>
<!-- 页面定义变量(本页赋值用name要加#取值),default默认值.-->
<!-- 赋值字符串要加'',不然是去Action中找对应的变量.value="test"->在Action中找test变量 -->
<s:set id="test1" value="'test1'"></s:set>
<!-- #request.get('javax.servlet.forward.context_path')必须经过Action转发到jsp才有值,直接访问jsp无值 -->
<s:set id="msg" value="#request.get('javax.servlet.forward.context_path')"/>
s:<s:property value="#msg" default="Struts2"/><br/>
el:${msg}<br/>

<!-- 取Action中变量的值不用加# -->
<s:property value="user.name"/><br/>

<!-- Action中的变量放在request中 -->
--${requestScope.user}||<br/>

<p><b>if标签:</b> If标签用来控制基本的条件处理流程,通常和else标签或者elseif标签连用。(%{}可加可不加)</p>
<!-- test必须,支持表达式(不支持el表达式) -->

<s:if test="#request.user!=null">

<div>user!=null</div>
<s:if test="%{#request.user.age>40}">
<div>中年人</div>
</s:if>
<s:elseif test="%{#request.user.age>20}">
<div>年轻人</div>
</s:elseif>
<s:else>
<div>小孩</div>
</s:else>

</s:if>
<s:else>
<div>user==null</div>
</s:else>
<p><b>iterator标签:</b> 对集合迭代 </p>
迭代List<br/>
<!-- 判断是否为空 -->
<s:if test="(userList!=null)&&(!userList.isEmpty())">
<div>userList</div>

<s:iterator value="#request.userList" id="u" status="st">
index:<s:property value="#st.index+1"/> 
是否第一个:<s:property value="#st.first"/> 
是否最后一个:<s:property value="#st.last"/> 
是否偶数:<s:property value="#st.even"/> 
是否奇数:<s:property value="#st.odd"/> 
<br/>
<s:property value="#u.name"/> 
<s:property value="#u.age"/> 
<!-- 格式化日期 -->
<s:date name="#u.birthday" format="yyyy-MM-dd HH:mm:ss" /> 
<br/>
</s:iterator>
</s:if>
<s:else>
<div>null</div>
</s:else>
迭代Map<br/>
<s:if test="(userMap!=null)&&(!userMap.isEmpty())">
<div>userMap</div>

<s:iterator value="#request.userMap" id="map" status="st">
index:<s:property value="#st.index+1"/> 
是否第一个:<s:property value="#st.first"/> 
是否最后一个:<s:property value="#st.last"/> 
是否偶数:<s:property value="#st.even"/> 
是否奇数:<s:property value="#st.odd"/> 
<br/>
<s:property value="#map.key"/> 
<s:property value="#map.value.name"/> 
<s:property value="#map.value.age"/> 
<!-- 格式化日期 -->
<s:date name="#map.value.birthday" format="yyyy-MM-dd HH:mm:ss" /> 
<br/>
</s:iterator>
</s:if>
<s:else>
<div>null</div>
</s:else>

<p><b>append标签:</b> 对多个集合进行合并 </p>
<br/>

合并List <br/>
<s:append id="newList">
<s:param value="#request.userList"></s:param>
<s:param value="#request.userList"></s:param>
</s:append>
<s:iterator value="#newList" id="u" status="st">
index:<s:property value="#st.index+1"/> 
是否第一个:<s:property value="#st.first"/> 
是否最后一个:<s:property value="#st.last"/> 
是否偶数:<s:property value="#st.even"/> 
是否奇数:<s:property value="#st.odd"/> 
<br/>
<s:property value="#u.name"/> 
<s:property value="#u.age"/> 
<!-- 格式化日期 -->
<s:date name="#u.birthday" format="yyyy-MM-dd HH:mm:ss" /> 
<br/>
</s:iterator>
<br/>

List和Map合并,结果为Map <br/>
<s:append id="newListMap">
<s:param value="#request.userList"></s:param>
<s:param value="#request.userMap"></s:param>
</s:append>

<s:iterator value="#newListMap" id="map" status="st">
index:<s:property value="#st.index+1"/> 
是否第一个:<s:property value="#st.first"/> 
是否最后一个:<s:property value="#st.last"/> 
是否偶数:<s:property value="#st.even"/> 
是否奇数:<s:property value="#st.odd"/> 
<br/>
<s:property value="#map.key"/> 
<s:property value="#map.value.name"/> 
<s:property value="#map.value.age"/> 
<!-- 格式化日期 -->
<s:date name="#map.value.birthday" format="yyyy-MM-dd HH:mm:ss" /> 
<br/>
</s:iterator>

<!-- 显示栈里的参数信息 -->
<s:debug></s:debug>


Struts2FormTaglib
Struts2表单标签
Updated
May 10, 2010 by
cm2...@gmail.com

<!--
action="userAction!%{tip}"动态方法tip为Action中的变量 submit不加method
-->
<s:form action="userAction" namespace="/form" method="POST">
<!-- userBean.id为Action中属性userBean中id的值 -->
<s:hidden name="userBean.id"></s:hidden>
<s:textfield name="userBean.name" label="用户名" ></s:textfield>
<s:textfield name="userBean.age" label="年龄" ></s:textfield>
<s:textfield name="userBean.birthday" label="生日" >
<!-- 格式化日期 -->
<s:param name="value">
<s:date name="userBean.birthday" format="yyyy-MM-dd" />
</s:param>
</s:textfield>
<!-- 密码不能回填 -->
<s:password name="userBean.password" label="密码" ></s:password>

<s:textarea name="userBean.des" label="描述" cols="35" rows="8"></s:textarea>

<!-- 这种checkbox显示出来是一行只有一个checkbox
<s:iterator value="likeList">
<s:checkbox name="userBean.likes" label="%{name}" fieldValue="%{id}"></s:checkbox>
</s:iterator>
-->
<!-- 这种是一行多个 -->
<s:checkboxlist list="likeList" name="userBean.likes" listKey="id" listValue="name" label="爱好"></s:checkboxlist>

<!-- 下拉框
<s:select list="likeList" listKey="id" listValue="name" headerKey="-1" headerValue="请选择爱好"></s:select>
-->
<!-- value="1" 设置默认,但是好像设置了不能回填 -->
<s:radio list="#{'1':'男','2':'女'}" label="性别"  name="userBean.sex"></s:radio>
<!-- value显示值,method调用方法 -->
<s:submit value="%{tip}" id="submitBut" method="%{tip}"></s:submit>
</s:form>


Struts2FileUpload
Struts2文件上传下载

Struts2
Updated
Apr 26, 2010 by
coderro...@gmail.com

文件上传

1.jsp页面

<s:form action="fileAction" namespace="/file" method="POST" enctype="multipart/form-data">
<!-- name为后台对应的参数名称 -->
<s:file name="files" label="file1"></s:file>
<s:file name="files" label="file2"></s:file>
<s:file name="files" label="file3"></s:file>
<s:submit value="提交" id="submitBut"></s:submit>
</s:form>

2.Action

//单个文件上传可以用 File files,String filesFileName,String filesContentType
//名称要与jsp中的name相同(三个变量都要生成get,set)
private File[] files;
// 要以File[]变量名开头
private String[] filesFileName;
// 要以File[]变量名开头
private String[] filesContentType;

private ServletContext servletContext;

//Action调用的上传文件方法
public String execute() {
ServletContext servletContext = ServletActionContext.getServletContext();
String dataDir = servletContext.getRealPath("/file/upload");
System.out.println(dataDir);
for (int i = 0; i < files.length; i++) {
File saveFile = new File(dataDir, filesFileName[i]);
files[i].renameTo(saveFile);
}
return "success";
}

3.配置上传文件临时文件夹(在struts.xml中配置)

<constant name="struts.multipart.saveDir" value="c:/temp"/>

文件下载

1.下载的url(到Action)

<a href="${pageContext.request.contextPath}/file/fileAction!down.action">下载</a>

2.struts.xml配置

<package name="file" namespace="/file" extends="struts-default">
<action name="fileAction" class="com.struts2.file.FileAction">
<!-- 下载文件配置 -->
<!--type 为 stream 应用 StreamResult 处理-->
<result name="down" type="stream">
<!--
不管实际类型,待下载文件 ContentType 统一指定为 application/octet-stream
默认为 text/plain
-->
<param name="contentType">application/octet-stream</param>
<!--
默认就是 inputStream,它将会指示 StreamResult 通过 inputName 属性值的 getter 方法,
比如这里就是 getInputStream() 来获取下载文件的内容,意味着你的 Action 要有这个方法
-->
<param name="inputName">inputStream</param>
<!--
默认为 inline(在线打开),设置为 attachment 将会告诉浏览器下载该文件,filename 指定下载文
件保有存时的文件名,若未指定将会是以浏览的页面名作为文件名,如以 download.action 作为文件名,
这里使用的是动态文件名,${fileName}, 它将通过 Action 的 getFileName() 获得文件名
-->
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<!-- 输出时缓冲区的大小 -->
<param name="bufferSize">4096</param>
</result>
</action>
</package>

3.Action

//Action调用的下载文件方法
public String down() {
return "down";
}

//获得下载文件的内容,可以直接读入一个物理文件或从数据库中获取内容
public InputStream getInputStream() throws Exception {
String dir = servletContext.getRealPath("/file/upload");
File file = new File(dir, "icon.png");
if (file.exists()) {
//下载文件
return new FileInputStream(file);

//和 Servlet 中不一样,这里我们不需对输出的中文转码为 ISO8859-1
//将内容(Struts2 文件下载测试)直接写入文件,下载的文件名必须是文本(txt)类型
//return new ByteArrayInputStream("Struts2 文件下载测试".getBytes());
}
return null;
}

// 对于配置中的 ${fileName}, 获得下载保存时的文件名
public String getFileName() {
String fileName ="图标.png";
try {
// 中文文件名也是需要转码为 ISO8859-1,否则乱码
return new String(fileName.getBytes(), "ISO8859-1");
} catch (UnsupportedEncodingException e) {
return "icon.png";
}
}


Struts2JSON
Struts2返回JSON
Updated
May 10, 2010 by
cm2...@gmail.com

1.导入jsonplugin包

Struts2.16:导入jsonplugin-0.34.jar包(下载包)和commons-logging-1.0.4.jar(Struts2 lib下有)

Struts2.18导入struts2-json-plugin-2.1.8.1.jar(Struts2 lib下有)

2.struts.xml中package 中extends="json-default"

<package name="json" namespace="/json" extends="json-default">

3.result 中type="json"

<!-- 封装所以的get开头的方法 -->
<result type="json" name="user">
</result>

<!-- 只包含user.id属性 -->
<result type="json" name="user">
<param name="includeProperties">
user\.id
</param>
</result>

<!-- 不包含user属性 -->
<result type="json" name="list">
<param name="excludeProperties">
user
</param>
</result>

<!-- 根对象只包含user -->
<result type="json">
<param name="root">
user
</param>
</result>

<!-- "root"对象中父类的field(属性)不会(会?) 默认存放到 JSON数据中,如果不想这样做,需要在配置时指定 ignoreHierarchy 为 false:  -->
<result type="json">
<param name="ignoreHierarchy">false</param>
</result>

4.避免使用get开头的action方法

在属性get方法上面加
@JSON(name="newName")json中的名称
@JSON(serialize=false) 属性不被加入json
@JSON(format="yyyy-MM-dd") 格式化日期

5.在action中赋值,返回对应的result字符串

Struts2XML
Struts2返回XML
Updated
May 10, 2010 by
cm2...@gmail.com

使用response

//直接调用responseOut方法
public void responseOut() throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
sb.append("<users>");
sb.append("     <user id=\"50\">");
sb.append("             <userName>abc</userName>");
sb.append("     </user>");
sb.append("</users>");

//得到response
HttpServletResponse response = ServletActionContext.getResponse();
//设置编码
response.setCharacterEncoding("UTF-8");
response.setContentType("text/xml;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
PrintWriter out = response.getWriter();
out.write(sb.toString());
out.flush();
out.close();
}


Struts2CRUD
Struts2增删改查例子
Updated
May 10, 2010 by
cm2...@gmail.com

UserBean

//要添加set,get
public class UserBean {
private Long id;
private String name;
private String password;
private int age;
private Date birthday;
private Long sex;
private Set<Long> likes;
private String des;//描述
}

public class LikeBean {
private Long id;
private String name;
}

FormAction

public class FormAction{
//属性要生成get,set
//标记(添加,修改)
private String tip;
private UserBean userBean;
private List<LikeBean> likeList;
private List<UserBean> userList;
//到列表页面
public String list(){
userList = new ArrayList<UserBean>();
userList.add(new UserBean("abc",19,new Date()));
userList.add(new UserBean("def",70,new Date()));
userList.add(new UserBean("ghi",40,new Date()));
return "list";
}
//到添加页面
public String toAdd(){
likeList = new ArrayList<LikeBean>();
likeList.add(new LikeBean(new Long(1),"上网"));
likeList.add(new LikeBean(new Long(2),"读书"));
likeList.add(new LikeBean(new Long(3),"游戏"));

tip = "add";
return "info";
}
//添加方法,Struts2会自动把对应的值赋值给userBean
public String add(){
System.out.println(userBean);
return "toList";
}
//到更新页面,取出值,设置标记
public String toUpdate(){
likeList = new ArrayList<LikeBean>();
likeList.add(new LikeBean(new Long(1),"上网"));
likeList.add(new LikeBean(new Long(2),"读书"));
likeList.add(new LikeBean(new Long(3),"游戏"));

tip = "update";
System.out.println("update name:"+userBean.getName());
userBean = new UserBean("update",100,new Date());
userBean.setId(new Long(101));
userBean.setSex(new Long(2));
userBean.setDes("Struts2用户");
Set<Long> likes = new HashSet<Long>();
likes.add(new Long(2));
likes.add(new Long(3));
userBean.setLikes(likes);
return "info";
}
//更新方法
public String update(){
System.out.println(userBean);
return "toList";
}
//删除方法
public String delete(){
System.out.println("del name:"+userBean.getName());
return "toList";
}
}

struts.xml配置

<struts>

<package name="form" namespace="/form" extends="struts-default">
<!-- 使用通配符调用多个方法,method为*对应的字符串 -->
<action name="userAction_*" class="com.struts2.form.FormAction" method="{1}">
<!-- 方法返回的字符串对应name,再转发到页面 -->
<result name="list">/form/list.jsp</result>
<result name="info">/form/info.jsp</result>
<!-- 重定向Action,可以有Action名字和空间,参数 -->
<result name="toList" type="redirectAction">
<!-- 空间参数,不写代表和当前Action同一个空间
<param name="namespace">form</param>
-->
<!-- 重定向Action的名称 -->
<param name="actionName">userAction_list</param>
<!-- url带的参数,${userBean.name}是动态参数,值为Action的属性userBean中name的值 -->
<param name="userName">${userBean.name}</param>
<param name="userAge">${userBean.age}</param>
</result>

</action>
</package>
</struts>

jsp页面

1.list(列表页面)

<!-- 添加标签 -->
<%@ taglib prefix="s" uri="/struts-tags" %>
<div><a href="${pageContext.request.contextPath}/form/userAction_toAdd.action">添加</a></div>
<div>
<!-- 判断用户是否为空 -->
<s:if test="(userList!=null)&&(!userList.isEmpty())">
<div>用户列表</div>
<!-- 遍历用户 -->
<s:iterator value="#request.userList" id="u" status="st">
<s:property value="#st.index+1"/> 
<s:property value="#u.name"/> 
<s:property value="#u.age"/> 
<!-- 格式化日期 -->
<s:date name="#u.birthday" format="yyyy-MM-dd HH:mm:ss" />    
<a href="${pageContext.request.contextPath}/form/userAction_toUpdate.action?userBean.name=${u.name}">修改</a>    
<a href="${pageContext.request.contextPath}/form/userAction_delete.action?userBean.name=${u.name}">删除</a>
<br/>
</s:iterator>
</s:if>
<s:else>
<div>无用户</div>
</s:else>
</div>

2.info.jsp(添加和修改页面)

<!-- 添加标签 -->
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- 显示标记(添加/修改) -->
<s:property value="tip"/>
<!-- action="userAction_%{tip}"tip为Action中的标记变量 submit不加method -->
<s:form action="userAction" namespace="/form" method="POST">
<!-- userBean.id为Action中属性userBean中id的值 -->
<s:hidden name="userBean.id"></s:hidden>
<s:textfield name="userBean.name" label="用户名" ></s:textfield>
<s:textfield name="userBean.age" label="年龄" ></s:textfield>
<s:textfield name="userBean.birthday" label="生日" >
<!-- 格式化日期 -->
<s:param name="value"><s:date name="userBean.birthday" format="yyyy-MM-dd" /></s:param>
</s:textfield>
<!-- 密码不能回填 -->
<s:password name="userBean.password" label="密码" ></s:password>
<s:textarea name="userBean.des" label="描述" cols="35" rows="8"></s:textarea>

<!-- 这种checkbox显示出来是一行只有一个checkbox
<s:iterator value="likeList">
<s:checkbox name="userBean.likes" label="%{name}" fieldValue="%{id}"></s:checkbox>
</s:iterator>
-->
<!-- 这种是一行多个 -->
<s:checkboxlist list="likeList" name="userBean.likes" listKey="id" listValue="name" label="爱好"></s:checkboxlist>

<!-- 下拉框
<s:select list="likeList" listKey="id" listValue="name" headerKey="-1" headerValue="请选择爱好"></s:select>
-->
<!-- value="1" 设置默认,但是好像设置了不能回填 -->
<s:radio list="#{'1':'男','2':'女'}" label="性别"  name="userBean.sex"></s:radio>
<s:submit value="%{tip}" id="submitBut" method="%{tip}"></s:submit>
</s:form>


Struts2I18n
Struts2国际化
Updated
May 10, 2010 by
cm2...@gmail.com

1.定义properties文件

资源文件查找顺序

JAVA国际化
如果系统同时存在资源文件、类文件,系统将以类文件为主,而不会调用资源文件。
对于简体中文的Locale,ResourceBundle搜索资源的顺序是:
(1)baseName_zh_CN.class
(2)baseName_zh_CN.properties
(3)baseName_zh.class
(4)baseName_zh.properties
(5)baseName.class
(6)baseName.properties

/* Struts2找不到对应语言的配置文件时,会先找系统语言对应的配置文件,再找不到才使用baseName.properties */

国际化分为三类:全局的,包级别的,类级别的

全局的:
1.在struts.xml中的<constant name="struts.custom.i18n.resources" value="message"></constant>指定baseName
国际化文件名为:baseName_语言名_国家名.properties(如:message_zh_CN.properties)
2.全局的国际化资源文件放在src下面

包级别的:
1.包级别的国际化资源文件放在该包下面
2.命名规则为:package_语言名_国家名.properties(如:package_zh_CN.properties)
其中package不变,不是指的是包名,每个包的国际化文件命名都这样

类级别的:
1.与该类放在同一目录中
2.命名规则为:类名_语言名_国家名.properties(如:RegisterAction_zh_CN.properties)

若同一key 在上面三个国际化文件中都为value值则:(优先级)类级别的>包级别的>全局的

2.在jsp中访问国际化资源文件

<!-- 输出国际化 -->
<!-- name为国际化文件中的key -->
<s:text name="hello">
<!-- 若该国际化文件的value中有{0}则可用下面的标签把参数传进去 -->
<s:param>mengya</s:param>
<s:param><s:property value="firstName"/></s:param>
</s:text>

<s:property value="%{getText('customer.contact')}"/>
传参数
<s:property value="%{getText('customer.contact','sh')}"/>

定义变量,加var属性
<s:text name="greetings" var="msg"/>
<s:property value="#msg"/>

<!-- 指定特定的国际化文件,name为全局国际化文件的baseName -->
<s:i18n name="temp">
<!-- 下面的<s:text>标签与上面的用法一样 -->
<s:text name="hello">
<s:param>mengya</s:param>
</s:text>
</s:i18n>

表单国际化:
1.theme不能为simple如:<s:form action="register">(默认的theme不是simple)
2.使用key如:<s:textfield name="username" key="" id="usernameId"></s:textfield>
3.或者使用getText方法<s:textfield name="username" label="%{getText('customer.name')}"/>

3.在Action中访问国际化资源文件,该Action继承了ActionSupport类

this.getText("username.invalid")
若该key对应的value需要参数则:
this.getText("username.invalid",new String[]{username})
或
List list = new ArrayList();
list.add(username);
this.getText("username.invalid",list)
如:
if (null == username || username.length() < 6 || username.length() > 10)
{
List list = new ArrayList();
list.add(username);
this.addActionError(this.getText("username.invalid",new String[]{username}));
}
该国际化资源文件中的key和value为:
username.invalid = \u7528\u6237\u540d "{0}" \u586b\u5199\u4e0d\u6b63\u786e

4.在输入验证框架访问国际化资源文件

<!-- 使用<message key="..."></message> ->
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="username.invalid"></message>
</field-validator>

5.动态改变语言

只有在url传入request_locale=en_US就可以指定为en_US的语言

1.如果配置文件是en_US结尾,传入en,en_US都可以找到

2.如果配置文件是en结尾,只能传入en,传入en_US会找不到,从而找默认的配置文件(en的范围>en_US,使用英语的国家不止美国一个)

<a  href="${pageContext.request.contextPath}/i18n/i18nAction?request_locale=en_US">English</a>

6.使用类来代替properties文件

//继承ListResourceBundle实现getContents方法
public class MyCustomResourceBundle extends ListResourceBundle {
public Object[][] getContents() {
if (Calendar.getInstance().get(Calendar.HOUR_OF_DAY) < 12) {
return contents1;
} else {
return contents2;
}
}
static final Object[][] contents1 = {
{ "greetings", "中午好 {0}" },
{ "farewell", "再见" } };

static final Object[][] contents2 = {
{ "greetings", "你好 {0}" },
{ "farewell", "再见" } };
}

//en对应的字符串
public class MyCustomResourceBundle_en extends ListResourceBundle {
public Object[][] getContents() {
if (Calendar.getInstance().get(Calendar.HOUR_OF_DAY) < 12) {
return contents1;
} else {
return contents2;
}
}
static final Object[][] contents1 = {
{ "greetings", "Good morning {0}" },
{ "farewell", "Good bye" } };

static final Object[][] contents2 = {
{ "greetings", "Hello {0}" },
{ "farewell", "Good bye" } };
}

//jsp中使用
<s:i18n name="com.resourcebundle.MyCustomResourceBundle">
<s:text name="greetings">
<s:param>Jon</s:param>
</s:text>.
<s:text name="farewell"/>
</s:i18n>


Struts2Validator
Struts2验证框架
Updated
Jun 18, 2010 by
cm2...@gmail.com

Action配置中一定要设置input返回页面

添加验证只要创建验证的xml文件

1.创建xml文件名

验证Action中全部方法

在Action同包下,创建:Action类名-validation.xml

如:ValidateAction创建ValidateAction-validation.xml

验证Action中单个方法

<!-- 每个方法单独配置一个Action -->
<!-- 在Action同包下,创建:Action类名-action方法配置名称-validation.xml -->
<action name="validateAdd" class="com.struts2.validator.ValidateAction" method="add">
<!-- 要创建ValidateAction-validateAdd-validation.xml -->

<!-- 使用通配符配置 -->
<!-- 在Action同包下,创建:Action类名-action方法对应的名称-validation.xml -->
<action name="validate_*" class="com.struts2.validator.ValidateAction" method="{1}">
<!-- 要创建ValidateAction-validate_add-validation.xml,validate_add为访问这个action方法的路径 -->

注意事项

注意:
1.要验证的方法不能叫input.
2.这样配置在form表单中要在<s:form action="validate_add">中action写好名称,
不能写action="validate_",然后<s:submit value="提交"method="add" />
这样会找不到对应的配置文件,跳过验证.
3.如果验证出错,返回input页面时,那些存在ValueStack中的值会丢失,可以将Action实现Preparable接口,
然后prepare()方法里初始化添加页面需要的值.
4.如果使用Preparable接口,必须在action配置中添加<interceptor-ref name="paramsPrepareParamsStack" />.
这样prepare()才能得到form提交的参数.

2.创建xml内容

<!DOCTYPE validators PUBLIC  "-//OpenSymphony Group//XWork Validator 1.0.2//EN"  "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

<validators>
<!-- 要验证的字段名称 -->
<!-- 要验证Action中UserBean的id字段,name="userBean.id"(userBean为Action中的变量名) -->
<field name="mail">
<!-- type要验证的类型,short-circuit(默认false),true含义,如果当前验证失败了,下面的验证就不执行了.如requiredstring失败了,email就不验证了. -->
<!-- field-validator下面可以有多个param元素,但是最多只能有一个message -->
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>Please enter a mail</message>
</field-validator>
<field-validator type="email">
<message>
Invalid MAIL
</message>
</field-validator>
</field>
</validators>

Struts内建验证程序(type的值)

required

保证字段的值不是空值null.空字符串不是空值null.

<field name="userName">
<field-validator type="required">
<message>Please enter a user name</message>
</field-validator>
</field>

requiredstring

保证字段不是空值null,也不是空白(empty).

param:trim(boolean) ->true->去除前后空格

<field name="userName">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>Please enter a user name</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<param name="trim">false</param>
<message>Please enter a password</message>
</field-validator>
</field>

int

验证字段值是否可以转换为一个整数.

param: min(int);max(int)

<field name="yeaar">
<field-validator type="int">
<param name="min">1999</param>
<param name="max">2010</param>
<message>year:1999-2010</message>
</field-validator>
</field>

date

验证给定日期字段的值是否在一个给定的范围内.

param:max(date);min(date)

<field name="borthday">
<field-validator type="int">
<!-- 格式取决于当前地理时区 -->
<param name="min">1999-01-01</param>
<param name="max">2010-01-01</param>
<message>birthday:1999-2010</message>
</field-validator>
</field>

email

给定的String值是否是一个电子邮件地址

<field name="email">
<field-validator type="email">
<message>Invalid email</message>
</field-validator>
</field>

url

给定的String值是否是一个合法的URL(要有前缀)

<field name="url">
<field-validator type="url">
<message>Invalid URL</message>
</field-validator>
</field>

expression,fieldexpression

验证给定字段是否满足一个OGNL表达式.

区别:expression 不是一个字段验证程序,失败时将生成一个动作错误.(JSP中调用

<actionerror>

</actionerror>

才显示出错信息)

fieldexpression 是一个字段验证程序,失败时将抛出一个字段错误.(对字段验证)

param:expression(String)OGNL表达式
expression:

public class ExpressionTestAction {
//属性生成get,set
private int min;
private int max;
}
<validator type="expression">
<param name="expression">
max > min
</param>
<message>
Maximum temperature must be greater than Minimum temperature
</message>
</validator>

<!-- jsp -->
<s:actionerror/>

fieldexpression:

public class FieldExpressionTestAction {
//属性生成get,set
private int min;
private int max;
}
<!-- 对字段验证 -->
<field name="max">
<field-validator type="fieldexpression">
<param name="expression">
max > min
</param>
<message>
Maximum temperature must be greater than Minimum temperature
</message>
</field-validator>
</field>

visitor

把同一个验证程序配置文件用于多个动作(对一个Bean写验证文件,每个使用的Action只要引用)

//UserBean
public class UserBean {
//属性get,set
private String name;
private int age;
}

//UserBean-validation.xml(和UserBean放在同一个包中)
<field name="name">
<field-validator type="requiredstring">
<message>用户名必须</message>
</field-validator>
</field>
<field name="age">
<field-validator type="int">
<param name="min">18</param>
<param name="max">99</param>
<message>Age must be between 18 and 99</message>
</field-validator>
</field>

//Action的validation.xml
<!-- userBean变量名 -->
<field name="userBean">
<field-validator type="visitor">
<!-- message会和UserBean验证中的message一起显示 -->
<message>用户: </message>
</field-validator>
</field>

如果另一个Action对UserBean使用另一个标准的验证,可以创建新的验证文件

//UserBean-specific-validation.xml
<!-- 和之前的验证不同 -->
<field name="age">
<field-validator type="int">
<param name="min">30</param>
<param name="max">50</param>
<message>Age must be between 30 and 50</message>
</field-validator>
</field>
//另一个Action的validation.xml
<field name="userBean">
<field-validator type="visitor">
<!-- xml中扩展的名字,执行UserBean-specific-validation.xml的验证 -->
<param name="context">specific</param>
<message>用户1: </message>
</field-validator>
</field>

conversion

检查对某个属性进行类型转换是否会导致一个转换错误

<field name="age">
<field-validator type="conversion">
<message>
An age must be an integer.
</message>
</field-validator>
</field>

stringlength

验证一个非空的字段值是不是足够的长度

param:minLength(int);maxLength(int);trim(boolean)

<field name="password">
<field-validator type="requiredstring">
<param name="minLength">6</param>
<param name="maxLength">14</param>
<message>length:6-14</message>
</field-validator>
</field>

regex

给定的值是否与一个给定的正则表达式匹配

param:expression(String)正则表达式;caseSensitive(boolean)是否区别大小写,默认为true;trim(boolean)是否去除前后空格

<field name="phone">
<field-validator type="regex">
<param name="expression">
<![CDATA[\d\d\d\-\d\d\d\-\d\d\d\d]]>
</param>
<message>
Invalid phone number or invalid format
</message>
</field-validator>
</field>

3.在action中验证

利用Validateable接口实现验证,实现void validate()方法.

ActionSupport类已经实现了这个接口

//继承ActionSupport
public class User extends ActionSupport {
//属性get,set
private String userName;
private String password;
private static List<String> userNames = new ArrayList<String>();
static {
userNames.add("harry");
userNames.add("sally");
}
//验证方法
public void validate() {
if (userNames.contains(userName)) {
//添加出错信息
addFieldError("userName",  "'" + userName + "' has been taken.");
}
}
}

4.自定义验证类

要创建一个普通的验证程序(非字段验证程序),扩展ValidatorSupport类.验证失败要从validate方法调用addActionError方法.

要创建一个字段验证程序,扩展FieldValidatorSupport类.验证失败要从validate方法调用addFieldError方法.

如果要能接受参数,要在类中定义一个相应的属性,并生成get,set.

编写类

public class StrongPasswordValidator extends FieldValidatorSupport {
//属性
private int minLength = -1;
public void setMinLength(int minLength) {
this.minLength = minLength;
}
public int getMinLength() {
return minLength;
}
//验证方法
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
String value = (String) getFieldValue(fieldName, object);
if (value == null || value.length() <= 0) {
// use a required validator for these
return;
}
if ((minLength > -1) && (value.length() < minLength)) {
addFieldError(fieldName, object);
} else if (!isPasswordStrong(value)) {
addFieldError(fieldName, object);
}
}

private static final String GROUP_1 = "abcdefghijklmnopqrstuvwxyz";
private static final String GROUP_2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String GROUP_3 = "0123456789";
protected boolean isPasswordStrong(String password) {
boolean ok1 = false;
boolean ok2 = false;
boolean ok3 = false;
int length = password.length();
for (int i = 0; i < length; i++) {
if (ok1 && ok2 && ok3) {
break;
}
String character = password.substring(i, i + 1);
System.out.println("character:" + character);
if (GROUP_1.contains(character)) {
ok1 = true;
continue;
}
if (GROUP_2.contains(character)) {
ok2 = true;
continue;
}
if (GROUP_3.contains(character)) {
ok3 = true;
}
}
return (ok1 && ok2 && ok3);
}
}

注册xml

在src下创建validators.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC  "-//OpenSymphony Group//XWork Validator Config 1.0//EN"  "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">

<validators>
<!-- 名称(type对应值),类路径 -->
<validator name="strongpassword"  class="com.validator.StrongPasswordValidator"/>
</validators>

使用验证

<field name="password">
<field-validator type="strongpassword">
<param name="minLength">8</param>
<message>
Password must be at least 8 characters long
and contains at least one lower case character,
one upper case character, and a digit.
</message>
</field-validator>
</field>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: