struts2总结
来源:互联网 发布:2017年网络词汇 编辑:程序博客网 时间:2024/04/28 09:26
struts2了解
1.到Apache下载Struts 2.0包 http://people.apache.org/builds/struts/2.0.10/
2.将Struts 2.0 lib下的jar文件加到工程的构建路径(/WEB-INF/lib)--commons-logging-1.0.4.jar,freemarker-2.3.8.jar,ognl-2.6.11.jar,struts2-core-2.0.10.jar,xwork-2.0.4.jar
3.在src下新建struts.xml文件--内容如下:
<!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"/> </struts>
4.修改web.xml文件--增加内容如下:
<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>
5.新建UserAction继承ActionSupport--并修改struts.xml如下:
<package name="struts2Demo1" extends="struts-default">
<action name="user" class="control.UserAction"><result>success.jsp</result></action>
</package>
6.UserAction定义UserInfo类的变量来接收表单的数据.页面表单定义要如下L
<s:form action="user" method="post"> name:<s:textfield name="user.name" ></s:textfield> password <s:password name="user.password"></s:password> <s:submit name="submit" value="submit"></s:submit>
</s:form>
7.STRUTS2转换器
配置全局的类型转换器,在源代码文件夹下,新建一个名为“xwork-conversion.properties”的配置文件,并在文件中加入“待转换的类型的全名(包括包路径和类名)=转换器类的全名”对;应用于某个特定类的类型转换器,做法为在该类的包中添加一个格式为“类名-conversion.properties”的配置文件,并在文件中加入“待转换的属性的名字=转换器类的全名”对。常用到的转换器,如日期、整数或浮点数等类型,Struts 2.0已经为您实现
8.STRUTS2校验框架
在Action类所在包下新建校验配置文件Xxx-validation.xml(Xxx为Action的类名),内容如下:
<! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "<validators">http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"><validators> <field name ="reqiuredString"> <field-validator type ="requiredstring"> <message> This string is required </message> </field-validator > </field> </validators> --其中reqiuredString为表单控件的名字
9.Struts2默认的结果类型:dispatcher--struts.xml-<action/>---<result>标签内的type类型
10.JSP页面取得数据方式:Action类定义了属性list集合(要有GET方法),另种是存放到request对象userList(此名称映射到list集合).页面取数据有区别:<s:iterator id="u" value="list">,后一种<s:iterator id="u" value="#request.userList">
11.struts2资源国际化:全局资源文件(SRC目录)globalMessages.properties和globalMessages_xx_XX.properties,包资源文件package.properties和package_xx_XX.properties,ACTION资源文件:action类名.properties和action类名_xx_XX.properties,<s:i18n name="xxxxx">到</s:i18n>之间,所有的国际化字符串都会在名为xxxxx资源文件查找
查找顺序:ACTION资源--包资源--全局资源.页面显示方式:><s:text name="资源KEY"/>,><s:property value="%{getText('资源KEY')}"/>,<s:textfield name="name" label="%{getText('资源KEY')}"/>
12.参数化国际化字符串--在资源文件的国际化字符串中使用OGNL,格式为${表达式},例如:
validation.require=${getText(fileName)} is required
使用java.text.MessageFormat中的字符串格式,格式为{ 参数序号(从0开始), 格式类形(number | date | time | choice), 格式样式},例如:validation.between=Date must between {0, date, short} and {1, date, short}
使用标志的value0、value1...valueN的属性,如:<s:text name="validation.required" value0="User Name"/>
使用param子元素,这些param将按先后顺序,代入到国际化字符串的参数中,例如:
<s:text name="validation.required"><s:param value="User Name"/></s:text>
13.struts2中的ACTION类的非属性的GET方法外,其它方法不能以GET开头,否则每次请求ACTION时,都会自动调用以GET开头的方法.
struts2的开发(文件上传下载)
文件上传:
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。这两个文件可以从http://commons.apache.org/下载。
第二步:把form表的enctype设置为:“multipart/form-data“,如下:
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadFile">
</form>
第三步:在Action类中添加以下属性,属性红色部分对应于表单中文件字段的名称:
public class HelloWorldAction{
private File uploadFile;//得到上传的文件
private String uploadFileContentType;//得到文件的类型
private String uploadFileFileName;//得到文件的名称
//这里略省了属性的getter/setter方法
public String upload() throws Exception{
String realpath = ServletActionContext.getServletContext().getRealPath("/images");
File file = new File(realpath);
if(!file.exists()) file.mkdirs();
FileUtils.copyFile(uploadFile, new File(file, uploadFileFileName));
return "success";
}
}
多文件上传:
第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。这两个文件可以从http://commons.apache.org/下载。
第二步:把form表的enctype设置为:“multipart/form-data“,如下:
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">
<input type="file" name="uploadFile">
</form>
第三步:在Action类中添加以下属性,属性红色部分对应于表单中文件字段的名称:
public class HelloWorldAction{
private File[] uploadFiles;//得到上传的文件
private String[] uploadFilesContentType;//得到文件的类型
private String[] uploadFilesFileName;//得到文件的名称
//这里略省了属性的getter/setter方法
public String upload() throws Exception{
String realpath = ServletActionContext.getServletContext().getRealPath("/images");
File file = new File(realpath);
if(!file.exists()) file.mkdirs();
for(int i=0;i< uploadFiles.length;i++){
FileUtils.copyFile(uploadFiles[i], new File(file, uploadFilesFileName[i]));
}
return "success";
}
}
利用Stream结果视图实现文件下载:
实现步骤:
1.编写Action中的getInputStream()方法,将目标文件转化成输入流
public class DownloadAction extends ActionSupport{
private String fileName;
public InputStream getInputStream(){
InputStream inputStream=null;
try {
inputStream = new FileInputStream(new File(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return inputStream;
}
//fileName的getter和setter方法
public String execute() throws Exception {
return this.SUCCESS;
}
}
文件下载(配置struts.xml):
配置Struts.xml文件
<action name="download" class="com.xasxt.struts.action.DownloadAction">
<result type="stream">
<param name="contentType">application/actet-stream</param>
<param name="inputName">inputStream</param>
<param name="contentDisposition">attachment; filename="test.txt"</param>
<param name="bufferSize">4096</param>
</result>
</action>
对于Stream result来说,有以下四个参数
contentType:指定下载文件类型,其中application/octet-stream表示无限制
inputName:流对象名,例如inputstream,框架会自动去寻找action中的getInputStream方法.
contentDisposition:经过转码的文件名,默认格式为attachment;filename=“${fileName}”,调用该Action中的getFileName方法.
bufferSize:下载文件缓存大小
Struts2文件上传下载 --转载
--转载http://blog.csdn.net/icexuan007/article/details/7751570
要想将文件从客户端上传到服务器,首先要将form的enctype属性设置为” multipart/form-data”,method属性设置为post。然后在服务端获取上传的二进制流,解析出二进制流中所包含的全部表单域。表单域中包含的信息可能是文本信息,也可能是二进制的字节码,文本和字节码混合在一起。要想将一个文件中包含的二进制信息从一个二进制流中“抠”出来,我们需要编写大量的代码来处理字节,比较麻烦。我们可以使用一些编写好的文件上传框架来替我们完成从二进制流中提取表单域的工作。对于java而言,比较常用的框架有两个:common-FileUpload和COS,不管使用哪个上传框架,它都负责解析出HttpServletRequest请求中的所有域,不管是文件域还是普通的文本域。当框架替我们解析出文件域后,我们就可以使用流将文件内容写入到服务器中了。
这两个框架都能够完成文件上传,他们使用的方式有一些差异,但是它们都能够在struts2中使用。Struts2并没有提供自己的文件上传解析器,它需要借助于cos框架或者common-fileupload框架.这两个框架在使用的时候有差异,struts2对他们进行了封装,消除了这种差异。也就是说,在struts2中不管使用哪种文件上传框架,对于我们的编码没有任何影响。那么在struts2中,struts2如何知道使用的是哪种文件上传框架呢?在struts2中的核心包(struts2-core-2.3.4.jar)中的default.properties文件中我们可以看到如下的内容:
可见,在struts2中默认使用的是jakaarta的common-fileUpload进行文件上传的。如果要用它进行文件上传,就必须下载两个jar包
(commons-fileupload-1.2.2.jar,commons-io-2.0.1.jar)并加入到classpath中。上传文件大小最大不能超过2M(2097152byte=2M),当然这个值我们可以在struts.xml文件中进行修改。要想换成别的文件上传框架,只需将对应的jar包Copy到classpath中,然后修改常量struts.multipart.parser 即可
如果使用COS框架可去官方下载,它是oreilly组织下的一个小项目,该项目同样可以实现文件上传。
解压后Lib中有一个cos.jar文件就是我们需要使用的jar文件。
下面使用commons-FileUpload来进行文件上传:
这里我使用的是最新的Struts2.3.4,需要加入的包如图
JSP页面,index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>文件上传</title>
</head>
<body>
<s:form action="fileupload" enctype="multipart/form-data" method="post">
<s:textfield name="user" label="上传者" />
<s:file name="upfile" label="选择文件" />
<s:submit value="提交" />
</s:form>
</body>
</html>
编写Action:
package com.jason.web.action;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class FileUploadAction extends ActionSupport{
private static final long serialVersionUID = 2904016238332840263L;
private String user; //上传者
private File upfile; //上传的文件
private String upfileContentType; //文件类型
private String upfileFileName; //上传文件的原始名
private String savePath; //文件在服务器上的保存路径
//省略Getter、Setter方法
}
在strtus.xml中为这个action做配置
<struts>
<!-- 处理中文问题 ,默认配置就是UTF-8-->
<constant name="struts.i18n.encoding" value="UTF-8" />
<package name="com.jason" extends="struts-default">
<action name="fileupload" class="com.jason.web.action.FileUploadAction">
<!-- 注入属性savePath,表示文件上传位置 -->
<param name="savePath">/uploadFile</param>
<result name="disp">/show.jsp</result>
</action>
</package>
</struts>
web.xml需要加入struts2的filter
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Action中的execute方法
@Override
public String execute() throws Exception {
//获取web应用程序在服务器上的路径需要访问ServletAPI
ServletContext sctx=ServletActionContext.getServletContext();
//取得web应用在服务器上的物理路径:
//如"D:/apache-tomcat-6.0.16/webapps/struts2Demo/fileUpload"
String absoluteSavePath=sctx.getRealPath(savePath);
File saveDirectory=new File(absoluteSavePath);
//如果不存在,则创建
if(!saveDirectory.exists()){
saveDirectory.mkdir();
}
//保存的文件名
String saveName=this.createSaveFileName();
//存储到session中
ActionContext.getContext().getSession().put("file",savePath+"/"+saveName );
//保存文件
saveFile(new File(absoluteSavePath+"/"+saveName));
return "disp";
}
createSaveFileName方法
/**
<SPAN style="WHITE-SPACE: pre"> </SPAN>* 获取改名后的文件名
<SPAN style="WHITE-SPACE: pre"> </SPAN>* @return
<SPAN style="WHITE-SPACE: pre"> </SPAN>*/
<SPAN style="WHITE-SPACE: pre"> </SPAN>private String createSaveFileName(){
<SPAN style="WHITE-SPACE: pre"> </SPAN>//获取扩展名
<SPAN style="WHITE-SPACE: pre"> </SPAN>String extension=upfileFileName.substring(upfileFileName.indexOf('.'));
<SPAN style="WHITE-SPACE: pre"> </SPAN>//以系统时间的毫秒数作为文件名
<SPAN style="WHITE-SPACE: pre"> </SPAN>return System.currentTimeMillis()+extension;
<SPAN style="WHITE-SPACE: pre"> </SPAN>}
**/
/* 获取改名后的文件名
* @return
*/
private String createSaveFileName(){
//获取扩展名
String extension=upfileFileName.substring(upfileFileName.indexOf('.'));
//以系统时间的毫秒数作为文件名
return System.currentTimeMillis()+extension;
}
saveFile方法
/* 保存文件
* @param outputFile
*/
private void saveFile(File outputFile){
try {
FileOutputStream fos=new FileOutputStream(outputFile);
BufferedOutputStream bos=new BufferedOutputStream(fos);
FileInputStream fis=new FileInputStream(this.upfile);
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] buffer=new byte[1024];
int length=-1;
while((length=fis.read(buffer))!=-1){
bos.write(buffer,0,length);
}
bos.close();
fos.close();
bis.close();
fis.close();
}catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
show.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>显示页面</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
</head>
<body>
上传成功!相关信息如下:<br />
上传者:<s:property value="user"/> <br />
原始文件名:<s:property value="upfileFileName"/> <br />
文件类型:<s:property value="upfileContentType"/> <br />
保存后的文件:<s:property value="#session.file"/><br />
<img src="${pageContext.request.contextPath}${sessionScope.file}">
</body>
</html>
运行
文件过滤,我们可以限制文件的大小和格式
配置struts.xml
<struts>
<!-- 开发模式-->
<constant name="struts.devMode" value="true"/>
<!-- 处理中文问题 ,默认配置就是UTF-8-->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 处理.do结尾的请求 -->
<constant name="struts.action.extension" value="do"/>
<package name="com.jason" extends="struts-default">
<action name="fileupload" class="com.jason.web.action.FileUploadAction">
<!-- 注入属性savePath,表示文件上传位置 -->
<param name="savePath">/uploadFile</param>
<result name="disp">/show.jsp</result>
<interceptor-ref name="fileUpload">
<param name="allowedTypes">
image/bmp,image/png,image/gif,image/jpeg,image/pjpeg
</param>
<!--不能超过500K -->
<param name="maximumSize">512000</param>
</interceptor-ref>
<!-- 因为一旦为action配置了拦截器,
就不会使用默认的拦截器了,所以需要配置defaultStack-->
<interceptor-ref name="defaultStack" />
<!-- 出错之后,返回的地址 -->
<result name="input">/index.jsp</result>
</action>
</package>
</struts>
出错信息是默认输出的,如果需要输出自定义的错误消息,只需要在国际化的资源文件中配置即可。
Struts.xml中配置国际化资源文件的名称:
<!-- 配置国际化资源文件-->
<constant name="struts.custom.i18n.resources" value="messageResource"/>
messageResource.properties文件:
再测试:
多文件上传,可以使用数组或List集合实现多文件上传。表单中的文件域的name属性应该是一致的,加入name属性的值是“upload”,则Action中的属性应该这样来定义:
File[] upload
String[] uploadFileName
String[] uploadContentType
或者
List<File> upload
List<String> uploadFileName
List<String> uploadContentType
如果用户下载的文件是中文名,或者是在下载前检查用户的权限,就要使用Struts2提供的文件下载。在前台页面上提供一个链接,这个链接指向一个存在于服务器上的文件,并且文件的名字是中文名称,对中文名称进行URL编码。
以下是download.jsp文件的内容:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="java.net.URLEncoder"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>文件下载</title>
</head>
<body>
<a href="struts2download.action?fileName=<%=URLEncoder.encode("download中文.doc","UTF-8") %>">下载</a>
</body>
</html>
Action类:
package com.jason.web.action;
import java.io.InputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownLoadAction extends ActionSupport{
private static final long serialVersionUID = -6103161491484708345L;
private String fileName; // 要下载的文件的名称
private String downDir;// 下载文件夹由struts在配置文件中注入
private String fileEncoding;// 文件名 字符集
// 省略Getter、Setter方法
public InputStream getDownloadStream() throws Exception {
// 对于get方式提交的数据需要我们手工进行转码
fileName = new String(fileName.getBytes("iso-8859-1"), fileEncoding);
// 取得文件的路径
String filePath = downDir + "/" + fileName;
// System.out.println(filePath);
return ServletActionContext.getServletContext().getResourceAsStream(filePath);
}
@Override
public String execute() throws Exception {
return SUCCESS;
}
}
struts.xml配置文件
<action name="struts2download" class="com.jason.web.action.DownLoadAction">
<param name="downDir">/download</param>
<param name="fileEncoding">UTF-8</param>
<result type="stream">
<param name="inputName">downloadStream</param>
<!--下载文件的缓冲区大小-->
<param name="bufferSize">4096</param>
<!--
如果想让用户直接使用浏览器打开,我们可以直接指定正确的类型。
如果不好确定类型,可以使用application/octet-stream,表示二进制流
-->
<param name="contentType">application/octet-stream;charset=iso-8859-1</param>
<param name="contentDisposition">attachment;filename="${fileName}"</param>
</result>
</action>
运行OK!但是当取消下载时会报错
java.lang.IllegalStateException
异常原因分析: stream对应的类是org.apache.struts2.dispatcher.StreamResult,该类的处理过程如下:1。配置其中result标签下的各个参数2。从服务器中获取输入流,并同时与客户端建立输出流(服务器与客户端链接通过Socket进行连接)3。当点击“保存”或“打开”时,开始传输数据。如果点击“取消”,关闭所有的流。 这里要注意的是,但是实际发现Socket并没有断开!并且流也没有关闭!这一点非常重要! 所以在JSP容器通过Response获取输出流之前,前面的流并没有关闭,所以会造成该异常的报出。
解决办法:
需要下载struts2-sunspoter-stream-1.0.jar,并复制在/WEB-INF/lib下
struts.xml配置修改如下:
<result-types>
<result-type name="streamx" class="com.sunspoter.lib.web.struts2.dispatcher.StreamResultX"/>
</result-types>
<action name="struts2download" class="com.jason.web.action.DownLoadAction">
<param name="downDir">/download</param>
<param name="fileEncoding">UTF-8</param>
<result type="streamx">
<param name="inputName">downloadStream</param>
<!--下载文件的缓冲区大小-->
<param name="bufferSize">4096</param>
<!--
如果想让用户直接使用浏览器打开,我们可以直接指定正确的类型。
如果不好确定类型,可以使用application/octet-stream,表示二进制流
-->
<param name="contentType">application/octet-stream;charset=iso-8859-1</param>
<param name="contentDisposition">attachment;filename="${fileName}"</param>
</result>
</action>
只需添加一个result-type,将原有的result中type改为“streamx”,其他一律不变,在这种情况下,点击“取消”的同时也关闭了流,不会再报出该异常。
再点击取消会出现警告
WARN StreamResult:45 - StreamResultX Warn : socket write error
如果出现该警告说明正确执行,该警告说明,Socket非正常中断,但是流确实已经关闭,自此再也不用看到上面出现的讨厌异常结果。
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
<PRE></PRE>
Struts2.0实现上传文件进度条
原理:
利用Ajax在客户端一直查询服务器端的上传进度,取得进度的状态文本信息(xml,json格式的文本等),然后利用JS解析,显示在前台。
在Struts2. 0中,框架事先已经定义一种监听器:ProgressListener(进度监听器),里面有一个update(long readedBytes, long totalBytes, int currentItem)方法,其中,readedBytes是已经上传到服务器的位数,而totalBytes是上传文件总位数.当文件已二进制的方式上传时,每上传一部分数据,就会调用这个方法一次。故要实现监听进度,必须实现这个接口,并实现update方法,在update方法中保存这个进度到session。当客服端需要进度的信息时,只需要访问某个action,在这个action中读取session中保存的进度状态就可以了.
上传文件可大致分为两个阶段:1. 上传到服务器上,在临时目录中 2.从临时目录中把文件移到指定目录(由自己写的action处理),而struts2.的监听器只监听第一阶段。
实现:
(源代码下载: http://download.csdn.net/source/3568014)
第一步:
实现ProgressListener接口,实现update( )方法,详情见action包中的FileUploadListener.java 文件,里面有一个自定义的类:State ,它描述的是进度的状态,详情请看State注释。Update方法要做的就是不断地更新session中的state对象 代码如下:
<PRE class=html name="code">package action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
public class FileUploadListener implements ProgressListener{
private HttpSession session;
public FileUploadListener(HttpServletRequest request) {
session = request.getSession();
State state = new State();
session.setAttribute("state", state);
}
@Override
public void update(long readedBytes, long totalBytes, int currentItem) {
// TODO Auto-generated method stub
System.out.println("update:"+readedBytes+";"+totalBytes+";"+currentItem);
State state = (State) session.getAttribute("state");
state.setReadedBytes(readedBytes);
state.setTotalBytes(totalBytes);
state.setCurrentItem(currentItem);
}
}
</PRE><BR>
<PRE></PRE>
<P> </P>
<P>State类:</P>
<PRE class=html name="code">package action;
public class State {
private long readedBytes = 0L;/*已经上传的位数*/
private long totalBytes = 0L;/*文件所占位数*/
private int currentItem = 0;
private int rate=0; /*上传百分比*/
public long getReadedBytes() {
return readedBytes;
}
public void setReadedBytes(long readedBytes) {
this.readedBytes = readedBytes;
}
public long getTotalBytes() {
return totalBytes;
}
public void setTotalBytes(long totalBytes) {
this.totalBytes = totalBytes;
}
public int getCurrentItem() {
return currentItem;
}
public void setCurrentItem(int currentItem) {
this.currentItem = currentItem;
}
public int getRate() {
return rate;
}
public void setRate(int rate) {
this.rate = rate;
}
}</PRE>
<P><BR>
第二步:</P>
<P> 将监听器注入到struts2.0的MultiPartRequest封装类中,客户端发送request到服务器,struts2.0会将request封装成MultiPartRequest。因此必须将监听器注入到MultiPartRequest中。只需要在MultiPartRequest中加入以下两句:</P>
<P> </P>
<P>FileUploadListener progressListener = new FileUploadListener(servletRequest);</P>
<P>upload.setProgressListener(progressListener);//添加自己的监听器</P>
<P> </P>
<P>所以重新写一个新类MyMultiPartRequest代替MultiPartRequest ,代码与org.apache.struts2.dispatcher.multipart.MultiPartRequest 一样,在方法</P>
<P>private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) 中加入监听器.</P>
<P>如下:</P>
<P> </P>
<PRE class=html name="code">package action;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.struts2.StrutsConstants;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
public class MyMultiPartRequest implements MultiPartRequest{
static final Logger LOG = LoggerFactory.getLogger(MultiPartRequest.class);
// maps parameter name -> List of FileItem objects
protected Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();
// maps parameter name -> List of param values
protected Map<String,List<String>> params = new HashMap<String,List<String>>();
// any errors while processing this request
protected List<String> errors = new ArrayList<String>();
protected long maxSize;
@Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)
public void setMaxSize(String maxSize) {
this.maxSize = Long.parseLong(maxSize);
}
/**
* Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
* multipart classes (see class description).
*
* @param saveDir the directory to save off the file
* @param request the request containing the multipart
* @throws java.io.IOException is thrown if encoding fails.
*/
public void parse(HttpServletRequest request, String saveDir) throws IOException {
try {
processUpload(request, saveDir);
} catch (FileUploadException e) {
LOG.warn("Unable to parse request", e);
errors.add(e.getMessage());
}
}
private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
for (FileItem item : parseRequest(request, saveDir)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Found item " + item.getFieldName());
}
if (item.isFormField()) {
processNormalFormField(item, request.getCharacterEncoding());
} else {
processFileField(item);
}
}
}
private void processFileField(FileItem item) {
LOG.debug("Item is a file upload");
// Skip file uploads that don't have a file name - meaning that no file was selected.
if (item.getName() == null || item.getName().trim().length() < 1) {
LOG.debug("No file has been uploaded for the field: " + item.getFieldName());
return;
}
List<FileItem> values;
if (files.get(item.getFieldName()) != null) {
values = files.get(item.getFieldName());
} else {
values = new ArrayList<FileItem>();
}
values.add(item);
files.put(item.getFieldName(), values);
}
private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {
LOG.debug("Item is a normal form field");
List<String> values;
if (params.get(item.getFieldName()) != null) {
values = params.get(item.getFieldName());
} else {
values = new ArrayList<String>();
}
// note: see http://jira.opensymphony.com/browse/WW-633
// basically, in some cases the charset may be null, so
// we're just going to try to "other" method (no idea if this
// will work)
if (charset != null) {
values.add(item.getString(charset));
} else {
values.add(item.getString());
}
params.put(item.getFieldName(), values);
}
private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
/*自己新建监听器*/
FileUploadListener progressListener = new FileUploadListener(servletRequest);
upload.setProgressListener(progressListener);//添加自己的监听器
return upload.parseRequest(createRequestContext(servletRequest));
}
private DiskFileItemFactory createDiskFileItemFactory(String saveDir) {
DiskFileItemFactory fac = new DiskFileItemFactory();
// Make sure that the data is written to file
fac.setSizeThreshold(0);
if (saveDir != null) {
fac.setRepository(new File(saveDir));
}
return fac;
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()
*/
public Enumeration<String> getFileParameterNames() {
return Collections.enumeration(files.keySet());
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)
*/
public String[] getContentType(String fieldName) {
List<FileItem> items = files.get(fieldName);
if (items == null) {
return null;
}
List<String> contentTypes = new ArrayList<String>(items.size());
for (FileItem fileItem : items) {
contentTypes.add(fileItem.getContentType());
}
return contentTypes.toArray(new String[contentTypes.size()]);
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String)
*/
public File[] getFile(String fieldName) {
List<FileItem> items = files.get(fieldName);
if (items == null) {
return null;
}
List<File> fileList = new ArrayList<File>(items.size());
for (FileItem fileItem : items) {
File storeLocation = ((DiskFileItem) fileItem).getStoreLocation();
if(fileItem.isInMemory() && storeLocation!=null && !storeLocation.exists()) {
try {
storeLocation.createNewFile();
} catch (IOException e) {
if(LOG.isErrorEnabled()){
LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(),e);
}
}
}
fileList.add(storeLocation);
}
return fileList.toArray(new File[fileList.size()]);
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)
*/
public String[] getFileNames(String fieldName) {
List<FileItem> items = files.get(fieldName);
if (items == null) {
return null;
}
List<String> fileNames = new ArrayList<String>(items.size());
for (FileItem fileItem : items) {
fileNames.add(getCanonicalName(fileItem.getName()));
}
return fileNames.toArray(new String[fileNames.size()]);
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)
*/
public String[] getFilesystemName(String fieldName) {
List<FileItem> items = files.get(fieldName);
if (items == null) {
return null;
}
List<String> fileNames = new ArrayList<String>(items.size());
for (FileItem fileItem : items) {
fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
}
return fileNames.toArray(new String[fileNames.size()]);
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)
*/
public String getParameter(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
return v.get(0);
}
return null;
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()
*/
public Enumeration<String> getParameterNames() {
return Collections.enumeration(params.keySet());
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)
*/
public String[] getParameterValues(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
return v.toArray(new String[v.size()]);
}
return null;
}
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
*/
public List getErrors() {
return errors;
}
/**
* Returns the canonical name of the given file.
*
* @param filename the given file
* @return the canonical name of the given file
*/
private String getCanonicalName(String filename) {
int forwardSlash = filename.lastIndexOf("/");
int backwardSlash = filename.lastIndexOf("\\");
if (forwardSlash != -1 && forwardSlash > backwardSlash) {
filename = filename.substring(forwardSlash + 1, filename.length());
} else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
filename = filename.substring(backwardSlash + 1, filename.length());
}
return filename;
}
/**
* Creates a RequestContext needed by Jakarta Commons Upload.
*
* @param req the request.
* @return a new request context.
*/
private RequestContext createRequestContext(final HttpServletRequest req) {
return new RequestContext() {
public String getCharacterEncoding() {
return req.getCharacterEncoding();
}
public String getContentType() {
return req.getContentType();
}
public int getContentLength() {
return req.getContentLength();
}
public InputStream getInputStream() throws IOException {
InputStream in = req.getInputStream();
if (in == null) {
throw new IOException("Missing content in the request");
}
return req.getInputStream();
}
};
}
}
</PRE>
<P> </P>
<P> 在struts.xml中重新指定<EM>MultiPartRequest:</EM></P>
<P align=left> <bean type=<EM>"org.apache.struts2.dispatcher.multipart.MultiPartRequest"</EM> name=<EM>"requestParser"</EM></P>
<P align=left> class=<EM>"action.MyMultiPartRequest"</EM> scope=<EM>"default"</EM> optional=<EM>"true"</EM> /></P>
<P> <constant name=<EM>"struts.multipart.handler"</EM> value=<EM>"requestParser"</EM> /></P>
<P> </P>
<P>到这里,基本完成大部分工作.</P>
<P> </P>
<P>接下来是业务,写两个Action ,一个是查询进度的FileProgressAction ,一个是处理上传文件FileProgressUploadAction . FileProgressAction从Session中读入state对象,返回客户端。</P>
<P>在客户端方面,点击”提交”的时候,需要完成两步工作,1.不断到服务器查询进度2.上传文件,因为本人JS不是很好,就潦草写了一些,主要用的是Jquery框架。然后查询进度时,state对象以json数据方式传递到客户端的。</P>
<P>struts.xml文件:</P>
<PRE class=html name="code"><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<!-- 限定文件大小 -->
<constant name="struts.multipart.maxSize" value="500000000000000" />
<package name="upload" namespace="/upload" extends="json-default">
<!-- 查询上传进度 -->
<action name="progress" class="action.FileProgressAction">
<result name="success" type="json"></result>
</action>
<!-- 上传文件 -->
<action name="upload" class="action.FileProgressUploadAction">
<result name="success">/index.jsp</result>
<result name="input">/error.jsp</result>
</action>
</package>
<!-- 重新指定request封装类 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="requestParser"
class="action.MyMultiPartRequest" scope="default" optional="true" />
<constant name="struts.multipart.handler" value="requestParser" />
</struts> </PRE>
<P><BR>
前台页面:</P>
<PRE class=html name="code"><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script type="text/javascript" src="./js/jquery-1.4.min.js"></script>
<script>
var id=0;
function addressAction(){
$.post(
'./upload/progress.action',
function(data){
if(data.currentItem==0){
$("#m").text('0%');
}else if(data.state.rate!=100){
$("#m").text(data.state.readedBytes+'/'+data.state.totalBytes+':'+data.state.rate+'%');
}else{
$("#m").text(data.state.readedBytes+'/'+data.state.totalBytes+':'+'100% 上传完成!');
window.clearInterval(id);
}
$("#img").html("");
var num=data.state.rate/10;
for(var i=1;i<=num;i++){
$("#img").append("<img src='./images/grid.gif' />");
}
for(var j=1;j<=10-num;j++){
$("#img").append("<img src='./images/gray.gif' />");
}
},
'json'
);
}
function submitForm(){
if($("#f1").val()==""){
alert('上传文件为空!!!');
return ;
}
id=window.setInterval(addressAction,1000);
$("form:first").submit();/*提交第一个表单*/
}
</script>
</head>
<body>
<form action="./upload/upload.action" method="post" enctype="multipart/form-data">
File:<input type="file" name="file" id="f1" />
<br/>
<span id="m"></span><br/>
<span id="img"></span>
<br/>
<input type="button" onclick="submitForm()" value="上传" />
</form>
<br/>
</body>
</html></PRE>
<P><BR>
注意事项:</P>
<P> struts-json包要引进来</P>
<P> 使用Jquery框架 </P>
struts2文件上传的采用的三种方式解析
文件上传几乎是每个项目实现的一个必须的模块。
上传就是将信息从个人计算机(本地计算机)传递到中央计算机(远程计算机)系统上,让网络上的人都能看到。将制作好的网页、文字、图片等发布到互联网上去,以便让其他人浏览、欣赏。这一过程称为上传。
JAVA实现文件上传的几个组件:
1 SmartUpload 用的最多的一个组件,已经不再更新了,可以实现上传和下载
2 FileUpload Apache实现的文件上传组件,功能齐备
3 J2KUpload java2000实现的文件上传组件,全部使用内存,适合多个不超过10M的小文件
下面具体说说FileUpload Apache实现的文件上传组件。
1、/** 按copy方式上传 */
Java代码
public String uploadFile(){
/**保存的具体路径*/
String savepath = getSavePath();
/**根据保存的路径创建file对象*/
File file = new File(savepath);
if(!file.exists()){
/**创建此文件对象路径*/
file.mkdirs();
}
try {
/**使用的是:org.apache.commons.io.FileUtils FileUtils*/
FileUtils.copyFile(pic, new File(file,getPicFileName()));
} catch (IOException e) {
e.printStackTrace();
}
return SUCCESS;
}
备注:
1、getSavePath()方法中,ServletActionContext().getServletContext().getRealPath
(savePath+"\\"+getPicFileName()); ,这个主要是一个文件的实际路径
2、我个人认为这种方式是简单易用的。按copy方式上传使用的是Apache公司的
org.apache.commons.io.FileUtils包里的FileUtils.java。
2、/** 按字节方式上传 */
Java代码
public String uploadFile(){
/** 文件的写操作 */
FileInputStream fis = null;
FileOutputStream fos = null;
/** 保存的路径 */
String savepath = getSavePath();
/** 根据保存的路径创建file对象 */
File file = new File(savepath);
/** file对象是否存在 */
if (!file.exists()) {
/** 创建此文件对象路径 */
file.mkdirs();
}
try {
/** 创建输入流 */
fis = new FileInputStream(pic);
/** 输出流 更据文件的路径+文件名称创建文件对象 */
fos = new FileOutputStream(file + "//" + getPicFileName());
/** 读取字节 */
byte b[] = new byte[1024];
int n = 0;
/** 读取操作 */
while ((n = fis.read(b)) != -1) {
/** 写操作 */
fos.write(b, 0, n);
}
/** 关闭操作 */
if (fis != null) {
fis.close();
}
if (fos != null) {
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return SUCCESS;
}
3、/** 按字符方式上传 即“三层管道” */
Java代码
public String uploadFile(){
/** 文件的写操作 */
BufferedReader br =null;
BufferedWriter bw = null;
/** 保存的路径 */
String savepath = getSavePath();
/** 根据保存的路径创建file对象 */
File file = new File(savepath);
/** file对象是否存在 */
if (!file.exists()) {
/** 创建此文件对象路径 */
file.mkdirs();
}
try {
/** 创建一个BufferedReader 对象*/
br = new BufferedReader(new InputStreamReader(new FileInputStream
(pic)));
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream
(file + "//" + getPicFileName())));
// 读取字节
char b[] = new char[1024];
int n = 0;
// 读取操作
while ((n = br.read(b)) != -1) {
// 写操作
bw.write(b, 0, n);
}
// 关闭操作
if (br != null) {
br.close();
}
if (bw != null) {
bw.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return SUCCESS;
}
备注:
第二种上传方式没有第三种上传方式效率高。
建议:
最好用第一种方式上传,次之使用第三种方式上传,最后再使用第二种方式上传。
用命令实现Win7远程桌面关机和重启
关机 shutdown -s -t 0
重启 shutdown -r -t 0
打开运行框(Win+R键),输入上述命令即可,后面的数字表示关机/重启延迟的时间
at 12:00 shutdown -s
在运行里输入这个然后敲回车
他就会在12点种的时候关机
用法: shutdown [-i | -l | -s | -r | -a] [-f] [-m \computername] [-t xx] [-c "comment"] [-d up:xx:yy]没有参数 显示此消息(与 ? 相同)-i 显示 GUI 界面,必须是第一个选项-l 注销(不能与选项 -m 一起使用)-s 关闭此计算机-r 关闭并重启动此计算机-a 放弃系统关机-m \computername 远程计算机关机/重启动/放弃-t xx 设置关闭的超时为 xx 秒-c "别开bt了 谢谢" 关闭注释(最大 127 个字符)-f 强制运行的应用程序关闭而没有警告-d [p]:xx:yy 关闭原因代码 u 是用户代码 p 是一个计划的关闭代码 xx 是一个主要原因代码(小于 256 的正整数) yy 是一个次要原因代码(小于 65536 的正整数)-f:强行关闭应用程序-m \计算机名:控制远程计算机-i:显示图形用户界面,但必须是Shutdown的第一个选项-l:注销当前用户-r:关机并重启-t时间:设置关机倒计时-c "消息内容":输入关机对话框中的消息内容(不能超127个字符2、举例:关闭计算机 shutdown –s (方法:”开始”->”运行”->”shutdown -s”->”确定”)延迟3秒关闭计算机 shutdown –s –t 3(方法:”开始”->”运行”->”shutdown –s –t 3”->”确定”)取消关闭计算机 shutdown –a (方法:”开始”->”运行”->”shutdown –a”->”确定”)3、应用:(1)您如果想在1小时后自动关闭计算机,比如:您正在下载一个软件或一部电影,而您有急事要去丽水,您会怎么办?停止下载?太可惜了!继续?下载完后电脑怎么关闭?不用愁!您可以使用该命令解决:shutdown –s –t 3600 (系统在3600秒后关闭计算机,时间自定)您就放心地去丽水了,而不用担心您的计算机长期开着了。(2)出现RPC漏洞而导致倒计时自动关机,使用该命令取消倒计时自动关机:shutdown –a (方法:”开始”->”运行”->”shutdown –a”->”确定”)(3)利用Shutdown命令 实现网络统一关机 笔者所在单位下班后常有很多老师不关电脑,领导发现后要求解决这个问题。我想如果到各办公室去查看,这很费时,于是就想能否通过一台电脑把所有的机器都关掉。因为所有的办公用机安装的都是Windows XP系统,就想到了使用远程关机“Shutdown”命令。具体方法如下: 步骤1 单击“开始→运行”,在对话框中输入“Gpedit.msc”,单击[确定],打开“组策略编辑器”。 步骤2 在“组策略编辑器”窗口的左边打开“计算机配置→Windows设置→安全设置→本地策略→用户权利指派”,在右边的窗口选择“从远端系统强制关机”。在弹出的对话框中显示目前只有“Administrators”组的成员才有权远程关机;单击对话框下方的[添加用户或组]按钮,然后在弹出的对话框中输入“Heyongsheng”(管理员账号),再单击[确定]。 步骤3 这时在“从远端系统强制关机”的属性中便添加了一个“Heyongsheng”用户,单击[确定],最后关闭“组策略编辑器”窗口。 对各办公室的电脑进行上述操作后,我们便给每台计算机的“Heyongsheng”用户授予了远程关机的权限。到下班时,我只要在自己的机器上进行以下操作: 步骤4单击“开始→运行”,在对话框中输入“Shutdown -I”,屏幕上将显示“远程关机”对话框(图1)。 图1 远程关机控制窗口(4)用XP的ShutDown.exe实现服务器定时重启Windows XP的关机是由Shutdown.exe程序来控制的,位于WindowsSystem32文件夹中。如果想让Windows 2000也实现同样的效果,可以把Shutdown.exe复制到系统目录System32下。首先当然要求主板必须支持软件关机功能,否则你还得亲自去按电源开关才能关机,现在的主板一般都支持软件关机。操作步骤:单击“开始”/程序/附件/系统工具中"计划任务",弹出计划任务窗口,然后双击"添加已计划的任务",运行“计划任务向导”,按“浏览”,找到WINDOWS目录中的Rundll32.exe 文件,单击"打开"按钮,在出现的对话框中键入该任务的名字(如"定时关机"),执行方式选择“每天” ,然后按“下一步”选择定时关机时间(可以先试一下比现在机器上显示的时间晚1分钟),下一步选中“当单击完成打开该任务的高级属性”,单击"完成",最后右击"定时关机"来打开属性,在运行栏输入“C:WINNTsystem32shutdown.exe -r -t 1”,单击"确定"不久,OK了。4.结论:WindowsXP中的shutdown命令功能很强大,你可以去尝试使用,挖掘出功能,为我们服务
struts2中对异常的全局处理方法
Struts2中支持对异常进行全局处理 不用繁琐的在actionzhong try{}catch{}finally{}
struts.xml中的配置
<struts>
<package name="test" extends="struts-default">
//定义全局的逻辑结果与物理结果的映射
<global-results>
<result name="exception">/exception.jsp</result>
</global-results>
//定义异常的声明
<global-exception-mappings>
//result是结果处理result exception是catch中捕捉的异常类型
<exception-mapping result="exception" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
</package>
</struts>
在处理页面中可以通过以下两个方法打印异常类型和异常栈
<s:property value="exception"/>
<hr>
<s:property value="exceptionStack"/>
<hr>
Struts2中配置全局拦截器的方法
在struts.xml中添加如下配置:
<!-- 配置全局拦截器 -->
<package name="all" extends="struts-default">
<interceptors>
<!-- 定义权限控制拦截器 -->
<interceptor name="authority"
class="akai.cost.ms.base.AuthInterceptor" />
<!-- 定义一个包含权限控制的拦截器栈 -->
<interceptor-stack name="mydefault">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="authority" />
</interceptor-stack>
</interceptors>
<!-- 定义默认拦截器 -->
<default-interceptor-ref name="mydefault" />
<!-- 定义全局处理结果 -->
<global-results>
<!-- 逻辑名为login的结果,映射到/login.jsp页面 -->
<result name="login">/login.jsp</result>
</global-results>
</package>
使用方法:其他包继承这个包名就可以了
<package name="abc" extends="all" namespace="/">
附:拦截器类
package akai.cost.ms.base;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class AuthInterceptor extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
HttpSession session = ServletActionContext.getRequest().getSession();
String userName = (String)session.getAttribute("System_UserName");
if(userName == "" || userName == null){//错误,回到登录界面
return Action.LOGIN;
}else{
return invocation.invoke();
}
}
}
Struts2 i18n国际化(允许用户自行选择语言)
在很多成熟的商业软件中,可以让用户自由切换语言,当用户进入系统时候,可以出现一个下拉列表框,让用户选择语言,一旦用户选择了自己需要使用的语言环境,整个系统的语言环境将一直是这种语言环境。
Struts2也可以允许用户自行选择程序语言。而且,因为Struts2的支持,在程序中自行选择语言环境将变得更加简单。
一. Struts2国际化的运行机制
在Struts 2中,我们可以通过ActionContext.getContext().setLocale(Locale arg)可以设置用户的默认语言。不过,这种方式完全是一种手动方式,而且需要编程实现。
为了简化设置用户默认语言环境,Struts 2提供了一个名i18n的拦截器(Interceptor),并且将其住注册在默认的拦截器栈中(defaultStack)。下面是Struts2的默认拦截器栈的配置片段,代码中粗体字表示的就是i18n的拦截器
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servlet-config"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scoped-model-driven"/>
<interceptor-ref name="model-driven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="static-params"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
i18n拦截器在执行Action方法前,自动查找请求中的一个名为request_locale的参数。如果该参数存在,拦截器就将其作为参数,并转换成Locale对象,并将其设为用户默认的Locale(代表语言/国家环境)。
除此之外,i18n拦截器还会将上面生成的Locale对象保存在用户Session的名为“WW_TRANS_I18N_LOCALE”的属性中。一旦用户Session中存在一个名为“WW_TRANS_I18N_LOCALE”的属性,则该属性指定的Locale将会作为浏览者的默认Locale。
二. 创建下拉列表框
基于前面的介绍,为了实现让用户自行选择程序语言的功能,只需提供一个下拉列表框,让下拉列表框中列出本应用所支持的各种语言,并且,当用户选择下拉列表框中某一项时,系统将该下拉项的值作为request_locale参数提交给Struts2系统,
为此,我们将系统所支持的语言放入一个Map中,通过在JSP页面中迭代该Map对象,通过这种方式,就可以在页面上列出系统所支持的全部语言,并让用户自由选择。
下面定义了JavaBean,这个JavaBean里保存了当前应用所支持的全部语言,该JavaBean的代码如下:
//该JavaBean存放了系统所支持的全部语言。
public class Locales extends ActionSupport
{
//因为本实例也需要实现国际化,所以使用current作为用户当前的Locale
private Locale current;
//取得用户当前Locale的setter方法
public void setCurrent(Locale cur)
{
this.current = cur;
}
//取得本系统所支持的全部语言
public Map<String, Locale> getLocales()
{
//将当前系统支持的全部语言保持在Map对象中
Map<String, Locale> locales = new Hashtable<String, Locale>();
ResourceBundle bundle = ResourceBundle.getBundle("messageResource" , current);
//添加当前系统支持的语言,key是系统支持语言的显示名字,value是系统支持语言的Locale实例
locales.put(bundle.getString("usen"), Locale.US);
locales.put(bundle.getString("zhcn"), Locale.CHINA);
return locales;
}
}
在上面JavaBean中,我们使用了一个Map对象来保存所有用户支持的语言,该Map对象的key是所支持语言的显示名字,而该Map对象的value是所支持语言的Locale实例。应用支持语言的显示名字,也是通过国际化消息来生成的。
一旦定义了该JavaBean之后,就可以在JSP页面中创建该JavaBean的实例,并为其传入一个current参数,决定该JavaBean中的Locale参数,就可以根据该Locale参数来决定怎样显示系统所支持语言显示名称。
为了在JSP页面中使用该JavaBean实例,使用下面标签来创建该JavaBean实例。
<!-- 使用lee.Locales创建locales实例 -->
<s:bean id="locales" name="lee.Locales">
<!-- 为locales实例这种current参数值 -->
<s:param name="current"
value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"/>
</s:bean>
上面标签创建了lee.Locales类的locales实例,并为该实例传入了current参数值,设置该参数值时使用了三目运算符,先判断SESSION_LOCALE是否为空,如果该SESSION_LOCALE为空,则返回ValueStack中locale属性值(即用户浏览器设置的Locale);如果SESSION_LOCALE不为空,则返回该SESSION_LOCALE的值(即用户选择的Locale)。
为了让该页面中包含SESSION_LOCALE,使用Struts2的<s:set .../>标签将用户Session中的“WW_TRANS_I18N_LOCALE”属性值设置成SESSION_LOCALE。
下面是完成该设置的标签:
<!-- 将用户Session中的“WW_TRANS_I18N_LOCALE”属性值设置成SESSION_LOCALE。 -->
<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/>
下面是该selectlanguage.jsp页面的代码:
<%@taglib prefix="s" uri="/struts-tags"%>
<script type="text/javascript">
function langSelecter_onChanged()
{
document.getElementById("langForm").submit();
}
</script>
<!-- 将用户Session中的“WW_TRANS_I18N_LOCALE”属性值设置成SESSION_LOCALE。 -->
<s:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/>
<!-- 使用lee.Locales创建locales实例 -->
<s:bean id="locales" name="lee.Locales">
<!-- 为locales实例这种current参数值 -->
<s:param name="current"
value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"/>
</s:bean>
<!-- 让用户选择语言的表单 -->
<form action="<s:url/>" id="langForm"
style="background-color:#bbbbbb; padding-top: 4px; padding-bottom: 4px;">
<!-- 输出国际化提示 -->
<s:text name="languag"/>
<!-- 使用s:select标签迭代locales实例的locales Map属性 -->
<s:select label="Language" list="#locales.locales" listKey="value" listValue="key"
value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"
name="request_locale" id="langSelecter"
onchange="langSelecter_onChanged()" theme="simple"/>
</form>
注意:上面页面中大量使用了Struts2的标签,关于Struts2标签的详细用法,请参考本书第十章的讲解。
上面页面的原理是:使用<s:set .../>标签实例化一个Locales对象,并使用<s:select ..../>标前来显示该Locales对象的locales(Map类型)属性, <s:select ..../>可以使用一个下拉列表框来显示Map类型的集合,本应用将该Map的key输出成下拉列表项的显示名称,将该Map的value输出成下拉列表项的值。
除此之外,页面上面还有一段简单的Javascript脚本,它会在用户在选择下拉列表中某一项后,提交包含“reqeust_locale”变量的表单到Action。
上面页面和JavaBean一共使用了三个国际化key,所以需要在资源文件定义这三个key对应的国际化消息。因为本系统仅支持简体中文和美式英语两种环境(如需要增加其他语言也非常简单, 只需要增加更过的资源文件,并简单修改Locales类即可),所以需要分别在中文资源文件中增加如下三项:
languag=选择语言
usen=美式英语
zhcn=简体中文
在英文资源文件中增加如下三项:
languag=Select Lanuage
usen=American English
zhcn=Simplified Chinese
三. 选择程序语言
本应用为了更好的安全性,将所有的JSP页面都放在WEB-INF/jsp路径下,从而避免了直接访问JSP页面,为了让所有的JSP页面都能得到Struts2的处理,在struts.xml文件中增加如下配置片段:
<!-- 使用通配符号定义Action的name -->
<action name="*">
<!-- 将请求转发该WEB-INF/jsp路径下同名的JSP页面 -->
<result>/WEB-INF/jsp/{1}.jsp</result>
</action>
如果在浏览器中请求selectlanguage.action,将看到选择程序语言的页面。
如果用户通过上面的下拉列表框选择了“美式英语”项后,将看到用户选择美式英语的页面。
一旦定义了上面的页面后,我们就可以在JSP页面中通过<s:include .../>标签来包含该页面,包含该页面后,就可以自行选择程序语言了。
例如在登陆页面中通过如下标签来包含上面选择程序语言的页面:
<!-- 包含让用户自行选择程序语言的页面 -->
<s:include value="selectlanguage.jsp"/>
在任何页面中增加了上面代码后,该页面就能允许用户自行选择语言。通常,我们只需要在应用的第一个页面让用户选择程序语言,后续页面直接使用该语言即可。
如果浏览者在浏览器的地址栏访问input.action,Struts2自动进入WEB-INF/jsp/input.jsp页面,将看到允许用户选择程序语言的页面。
如果用户希望使用美式英语的语言,则在允许用户选择程序语言的页面的下拉列表框中选中“美式英语”项,将看到使用美式英语界面的页面。
如果用户登陆成功,将看到美式英语环境下登陆成功的页面。该页面自动使用了美式英语的环境,这都是因为用户选择了美式英语的语言后,系统将用户选择设置成了Session的WW_TRANS_I18N_LOCALE属性值,该Session属性值直接决定Struts2系统的语言环境。
---------!!!!!特别提醒:Locales 必须继承extends ActionSupport,这个是我折腾了一天的经验教训,网上有的就没继承的例子,不继承的话,导致<s:bean id="locales" name="lee.Locales">
<!-- 为locales实例这种current参数值 -->
<s:param name="current"
value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE"/>
</s:bean>
这里传值locale进去是空的,后边就会出问题。
Java实现多语言切换
一般在登录页或者首页的时候会有中英文连接
这个连接可以这样写:
- <div class="flag">
- <s:url id="localeUsUrl" action="localeUs"></s:url>
- <s:url id="localeCnUrl" action="localeCn"></s:url>
- <a class="en" href="${localeUsUrl }"><s:property value="getText('language.english')" /></a>
- <a class="ch" href="${localeCnUrl }"><s:property value="getText('language.chinese')" /></a>
- </div>
<div class="flag"><s:url id="localeUsUrl" action="localeUs"></s:url><s:url id="localeCnUrl" action="localeCn"></s:url><a class="en" href="${localeUsUrl }"><s:property value="getText('language.english')" /></a><a class="ch" href="${localeCnUrl }"><s:property value="getText('language.chinese')" /></a></div>
前台读取多语言:
其中 <s:property value="getText">标签是struts自带的可以获得properties里的键和值
当选中文时 后台action如下
- <SPAN style="WHITE-SPACE: pre"> </SPAN>session.clear();
- <SPAN style="WHITE-SPACE: pre"> </SPAN>session.put("WW_TRANS_I18N_LOCALE", Locale.CHINA);
- <SPAN style="WHITE-SPACE: pre"> </SPAN>ActionContext.getContext().setLocale(Locale.CHINA);
<span style="WHITE-SPACE: pre"></span>session.clear();<span style="WHITE-SPACE: pre"></span>session.put("WW_TRANS_I18N_LOCALE", Locale.CHINA);<span style="WHITE-SPACE: pre"></span>ActionContext.getContext().setLocale(Locale.CHINA);
英文:
- <SPAN style="WHITE-SPACE: pre"> </SPAN>session.clear();
- session.put("WW_TRANS_I18N_LOCALE", Locale.US);
- ActionContext.getContext().setLocale(Locale.US);
<span style="WHITE-SPACE: pre"></span>session.clear();session.put("WW_TRANS_I18N_LOCALE", Locale.US);ActionContext.getContext().setLocale(Locale.US);
后台读取多语言:
当我们从后台想输出一个提示信息时,首先要获取当前Local环境
- Locale currentLocale = Locale.getDefault();
Locale currentLocale = Locale.getDefault();
currentLocale 分en,zh等
读取properties的方法为:
- try {
- InputStream is = Test.class.getClassLoader().getResourceAsStream(propertiesName);
- properties.load(is);
- value = properties.getProperty(key);
- }
try { InputStream is = Test.class.getClassLoader().getResourceAsStream(propertiesName); properties.load(is); value = properties.getProperty(key); }
返回这个String 型的value即可
Struts2通过过滤器对每个jsp页面国际化
1、首先在web.xml中配struts2和一个过滤器(用来过滤所有jsp页面的请求)
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>
- org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
- </filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>*</url-pattern>
- <dispatcher>REQUEST</dispatcher> <!--这两行很重要不配的话无法转发到action-->
- <dispatcher>FORWARD</dispatcher>
- </filter-mapping>
- <filter>
- <filter-name>international</filter-name>
- <filter-class>
- com.i8ntest.filter.International <!--这个是我自己的包名和类名-->
- </filter-class>
- </filter>
- <filter-mapping>
- <filter-name>international</filter-name>
- <url-pattern>*.jsp</url-pattern>
- </filter-mapping>
2、在struts.xml中配置i18n的资源文件,i18n的国际化拦截器和负责处理jsp国际化的动态结果action(很拗口啊)
- <constant name="struts.custom.i18n.resources" value="find,whx,zxx"></constant>
- <!--配置properties文件,value中表示3个文件的开头名字-->
- <package name="com.i8ntest.action" extends="struts-default">
- <!--配置i18n的拦截器实现自动国际化-->
- <interceptors>
- <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
- </interceptors>
- <!--专用的jsp国际化action-->
- <action name="totalfilter" class="com.i8ntest.action.totalFilter">
- <result name="url" >${url}</result>
- </action>
- <!--测试用的设置语言国际化action-->
- <action name="setlang" class="com.i8ntest.action.SetLang">
- <result name="MyJsp">/MyJsp.jsp</result>
- </action>
- </package>
3、编写International过滤器过滤jsp请求统统转发到totalFilter.action
因为struts只能对走action的请求实现自动国际化所以要转发一下
- public class International implements Filter {
- public void destroy() {
- // TODO Auto-generated method stub
- }
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
- HttpServletRequest httpRequest=(HttpServletRequest)request;
- RequestDispatcher ds = request.getRequestDispatcher( "totalfilter.action");
- request.setAttribute("jsp", httpRequest.getServletPath());
- request.setAttribute("param", httpRequest.getQueryString());
- ds.forward(request, response);
- }
- public void init(FilterConfig filterConfig) throws ServletException {
- // TODO Auto-generated method stub
- }
- }
4、编写totalFilter.action实现动态跳转结果,实现jsp自动国际化
- public class TotalFilter extends ActionSupport {
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- private String url;
- public String getUrl() {
- return url;
- }
- public String execute() throws Exception{
- HttpServletRequest request = ServletActionContext.getRequest();
- Locale locale = (Locale)request.getSession().getAttribute("SessionLocale");
- if(locale != null){
- ActionContext.getContext().setLocale(locale);
- }
- String result = (String)request.getAttribute("jsp");
- url = result;
- if(request.getQueryString() != null){
- url = url + "?" + request.getQueryString();
- }
- return "url";
- }
- }
5、编写测试页面
setLang.action?request_locale=语言_国家(大写字母)
参数名必须是request_locale,i18n过滤器会自动获取参数更改session从而实现用户自己选择语言的功能
- <body>
- <a href="${pageContext.request.contextPath}/setlang.action?request_locale=zh_CN" >点我中文</a>
- <a href="${pageContext.request.contextPath}/setlang.action?request_locale=en_US" >clickme English</a>
- </body>
6、setLang.action其实很简单
- public class SetLang extends ActionSupport {
- public String execute() throws Exception{
- return "MyJsp";
- }
- }
7、MyJsp.jsp中使用struts标签库的s:text标签
- <body>
- <s:text name="find.language"></s:text><br>
- <s:text name="find.chargename"></s:text><br>
- <s:text name="navigation.location"></s:text><br>
- <s:text name="navigation.routesearch"></s:text><br>
- <s:text name="navigation.destinationselect"></s:text><br>
- <a href="${pageContext.request.contextPath}/1.jsp" >1</a>
- <a href="${pageContext.request.contextPath}/2.jsp" >2</a>
- </body>
struts2流程 应用
1、 多个Action共享一个视图--全局result配置
(1)当多个action中都使用到了相同视图,这时我们应该把result定义为全局视图。(2)struts1中提供了全局forward,struts2中也提供了相似功能:
<package ....>
<global-results>
<resultname="message">/message.jsp</result>
</global-results>
</package>
2、 为Action的属性注入值
(1)Struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,我们可以很方便地为Action中的属性注入值。注意:属性必须提供set方法。
(2)<paramname="savePath">/images</param>:通过<param>节点为action的savePath属性注入“/images”
3、指定需要Struts 2处理的请求后缀
(1)前面默认使用.action后缀访问Action。
(2)默认后缀是可以通过常量”struts.action.extension“进行修改的
<struts>
<constantname="struts.action.extension" value="do"/>
</struts>
(3)如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开
4、常量定义
(1)常量可在struts.xml或struts.properties中配置,建议在struts.xml中配置
(2)两种配置方式如下:
①在struts.xml文件中配置常量
<struts>
<constantname="struts.action.extension" value="do"/>
</struts>
②在struts.properties中配置常量
struts.action.extension=do
(3)因为常量可以在下面多个配置文件中进行定义,
所以需要了解struts2加载常量的搜索顺序:
struts-default.xml
struts-plugin.xml
struts.xml
struts.properties
web.xml
(4)如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值.
5、常用的常量
(1)<!--指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的输出 -->
<constantname="struts.i18n.encoding" value="UTF-8"/>
(2) <!--该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。
(3)如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 -->
<constantname="struts.action.extension" value="do"/>
(4) <!--设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 -->
<constantname="struts.serve.static.browserCache" value="false"/>
(5)<!--当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 -->
<constantname="struts.configuration.xml.reload" value="true"/>
(6) <!--开发模式下使用,这样可以打印出更详细的错误信息 -->
<constantname="struts.devMode" value="true" />
(7) <!--默认的视图主题 -->
<constantname="struts.ui.theme" value="simple" />
(8)<!–与spring集成时,指定由spring负责action对象的创建 -->
<constantname="struts.objectFactory" value="spring" />
(9)<!–该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false。 -->
<constant name="struts.enable.DynamicMethodInvocation"value="false"/>
(10)<!--上传文件的大小限制-->
<constant name="struts.multipart.maxSize" value=“10701096"/>
6、Struts2的处理流程
流程说明:
StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,负责拦截由<url-pattern>/*</url-pattern>指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求。默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入Struts 2框架处理,否则Struts 2框架将略过该请求的处理。当请求转入Struts 2框架处理时会先经过一系列的拦截器,然后再到Action。与Struts1不同,Struts2对用户的每一次请求都会创建一个Action,所以Struts2中的Action是线程安全的。
7、为应用指定多个struts配置文件
随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿。为了避免struts.xml文件过于庞大、臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。
struts.xml通过<include>元素指定多个配置文件:
<struts>
<includefile="struts-user.xml"/>
<includefile="struts-order.xml"/>
</struts>
8、动态方法调用
(1)如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法。
例子:
public classHelloWorldAction{
private String message;
....
public String execute() throws Exception{
this.message = "我的第一个struts2应用";
return "success";
}
public String other() throws Exception{
this.message = "第二个方法";
return "success";
}
}
假设action的URL路径为: /struts/test/helloworld.action
要访问action的other()方法,我们可以这样调用:
/struts/test/helloworld!other.action 这就是动态方法调用。
如不想使用动态方法调用,可以通过常量struts.enable.DynamicMethodInvocation关闭动态方法调用。
<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
9、使用通配符定义action
<packagename=“csdn” namespace="/test"extends="struts-default">
<action name="helloworld_*"class="cn.csdn.action.HelloWorldAction" method="{1}">
<resultname="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
public classHelloWorldAction{
private String message;
....
public String execute() throws Exception{
this.message = "我的第一个struts2应用";
return "success";
}
public String other() throws Exception{
this.message = "第二个方法";
return "success";
}
}
要访问other()方法,可以通过这样的URL访问:/test/helloworld_other.action
struts-default.xml,struts.xml,struts.properties文件详解
1) struts-default.xml
这个文件是struts2框架默认加载的配置文件。它定义struts2一些核心的bean和拦截器。
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!--struts2中工厂bean的定义-->
<bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
<bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="xwork" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/>
<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>
<!--类型检测bean的定义-->
<bean type="com.opensymphony.xwork2.util.ObjectTypeDeterminer" name="tiger" class="com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer"/>
<bean type="com.opensymphony.xwork2.util.ObjectTypeDeterminer" name="notiger" class="com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer"/>
<bean type="com.opensymphony.xwork2.util.ObjectTypeDeterminer" name="struts" class="com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer"/>
<!--文件上传bean的定义-->
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="struts" class="org.apache.struts2.dispatcher.mapper.DefaultActionMapper" />
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="composite" class="org.apache.struts2.dispatcher.mapper.CompositeActionMapper" />
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="restful" class="org.apache.struts2.dispatcher.mapper.RestfulActionMapper" />
<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="restful2" class="org.apache.struts2.dispatcher.mapper.Restful2ActionMapper" />
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" optional="true"/>
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" optional="true" />
<!--标签库bean的定义-->
<bean type="org.apache.struts2.views.TagLibrary" name="s" class="org.apache.struts2.views.DefaultTagLibrary" />
<!--一些常用视图bean的定义-->
<bean class="org.apache.struts2.views.freemarker.FreemarkerManager" name="struts" optional="true"/>
<bean class="org.apache.struts2.views.velocity.VelocityManager" name="struts" optional="true" />
<bean class="org.apache.struts2.components.template.TemplateEngineManager" />
<bean type="org.apache.struts2.components.template.TemplateEngine" name="ftl" class="org.apache.struts2.components.template.FreemarkerTemplateEngine" />
<bean type="org.apache.struts2.components.template.TemplateEngine" name="vm" class="org.apache.struts2.components.template.VelocityTemplateEngine" />
<bean type="org.apache.struts2.components.template.TemplateEngine" name="jsp" class="org.apache.struts2.components.template.JspTemplateEngine" />
<!--类型转换bean的定义-->
<bean type="com.opensymphony.xwork2.util.XWorkConverter" name="xwork1" class="com.opensymphony.xwork2.util.XWorkConverter" />
<bean type="com.opensymphony.xwork2.util.XWorkConverter" name="struts" class="com.opensymphony.xwork2.util.AnnotationXWorkConverter" />
<bean type="com.opensymphony.xwork2.TextProvider" name="xwork1" class="com.opensymphony.xwork2.TextProviderSupport" />
<bean type="com.opensymphony.xwork2.TextProvider" name="struts" class="com.opensymphony.xwork2.TextProviderSupport" />
<!-- Struts2中一些可以静态注入的bean,也就是不需要实例化的 -->
<bean class="com.opensymphony.xwork2.ObjectFactory" static="true" />
<bean class="com.opensymphony.xwork2.util.XWorkConverter" static="true" />
<bean class="com.opensymphony.xwork2.util.OgnlValueStack" static="true" />
<bean class="org.apache.struts2.dispatcher.Dispatcher" static="true" />
<bean class="org.apache.struts2.components.Include" static="true" />
<bean class="org.apache.struts2.dispatcher.FilterDispatcher" static="true" />
<bean class="org.apache.struts2.views.util.ContextUtil" static="true" />
<bean class="org.apache.struts2.views.util.UrlHelper" static="true" />
<!-- 定义Struts2默认包-->
<package name="struts-default" abstract="true">
<!-- 结果类型的种类-->
<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" />
<result-type name="redirect-action" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="plaintext" class="org.apache.struts2.dispatcher.PlainTextResult" />
</result-types>
<!--struts2中拦截器的定义-->
<interceptors>
<!--实现在不同请求中相似参数别名的准换-->
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<!--与Spring整合时自动装配的拦截器-->
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<!--构建一个action链,使当前action可以访问前一个action,与<result-type="chain" />配合使用-->
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<!--负责类型转换的拦截器-->
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
<!--使用配置的name,value来是指cookies -->
<interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
<!--负责创建httpSession-->
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
<!--输出调试信息-->
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
<!--扩展引用-->
<interceptor name="externalRef" class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
<!--后台执行action负责发送等待画面给用户-->
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<!--异常处理-->
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<!--文件上传,解析表单域的内容-->
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<!--支持国际化-->
<interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
<!--日志记录-->
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
<!--模型拦截器,当action实现了ModelDriven接口时,负责把getModel的结果放入valueStack中-->
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<!--有生命周期的ModelDriven-->
<interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<!--负责解析请求中的参数,并赋值给action中对应的属性-->
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<!--实现该Preparable接口的action,会调用拦截器的prepare方法-->
<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
<!--负责将action 标签下的param参数值传递给action实例-->
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<!--范围转换-->
<interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
<!--用于访问Servlet API-->
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="sessionAutowiring" class="org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor"/>
<!--输出action执行时间-->
<interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
<!--防止表单重复提交-->
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
<!--与token拦截器相似,只是把token保存到HttpSession-->
<interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
<!--负责表单字段的验证 *-validation.xml-->
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
<!--负责执行action的validate()-->
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
<!--存储和重新获取Action 消息/错误/字段错误为Action,实现ValidationAware接口到seesion-->
<interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<!--添加自动checkbox处理代码,这样检探测checkbox和添加它作为一个参数使用默认值(通常’false’).使用一个指定名字隐藏字段探测没提交的checkbox-->
<interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
<interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
<!--JAAS服务拦截器-->
<interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
<!-- 一个基本的拦截器栈 -->
<interceptor-stack name="basicStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>
<!-- 简单的validtion和webflow栈 -->
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>
<!-- 文件上传的拦截器栈 -->
<interceptor-stack name="fileUploadStack">
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- model-driven 栈 -->
<interceptor-stack name="modelDrivenStack">
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- action链的拦截器栈 -->
<interceptor-stack name="chainStack">
<interceptor-ref name="chain"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- i18n 拦截器栈 -->
<interceptor-stack name="i18nStack">
<interceptor-ref name="i18n"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- 结合preparable和ModenDriven拦截器-->
<interceptor-stack name="paramsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="params"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
</interceptor-stack>
<!--定义默认的拦截器栈 -->
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo/..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
<interceptor-stack name="completeStack">
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
<interceptor-stack name="executeAndWaitStack">
<interceptor-ref name="execAndWait">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="execAndWait">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
</interceptor-stack>
<interceptor name="external-ref" class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
<interceptor name="model-driven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="static-params" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="scoped-model-driven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<interceptor name="servlet-config" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="token-session" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
</interceptors>
<!--定义默认拦截器为"defaultStack"-->
<default-interceptor-ref name="defaultStack"/>
</package>
</struts>
2) struts.xml
该文件也是struts2框架自动加载的文件,在这个文件中可以定义一些自己的action,interceptor,package等,该文件的package 通常继承struts-default包。下面是这个文件的格式。
struts.properties文件
<!-- 下面指定Struts 2配置文件的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!-- struts是Struts 2配置文件的根元素 -->
<struts>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<constant name="" value="" />
<!-- 下面元素可以出现0次,也可以无限多次 -->
<bean type="" name="" class="" scope="" static="" optional="" />
<!-- 下面元素可以出现0次,也可以无限多次 -->
<include file="" />
<!-- package元素是Struts配置文件的核心,该元素可以出现0次,或者无限多次 -->
<package name="必填的包名" extends="" namespace="" abstract=""
externalReferenceResolver>
<!-- 该元素可以出现,也可以不出现,最多出现一次 -->
<result-types>
<!-- 该元素必须出现,可以出现无限多次-->
<result-type name="" class="" default="true|false">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</result-type>
</result-types>
<!-- 该元素可以出现,也可以不出现,最多出现一次 -->
<interceptors>
<!-- 该元素的interceptor元素和interceptor-stack至少出现其中之一,
也可以二者都出现 -->
<!-- 下面元素可以出现0次,也可以无限多次 -->
<interceptor name="" class="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</interceptor>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<interceptor-stack name="">
<!-- 该元素必须出现,可以出现无限多次-->
<interceptor-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<default-interceptor-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>
</default-interceptor-ref>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<default-action-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</default-action-ref>?
<!-- 下面元素可以出现0次,也可以无限多次 -->
<global-results>
<!-- 该元素必须出现,可以出现无限多次-->
<result name="" type="">
<!-- 该字符串内容可以出现0次或多次 -->
映射资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</result>
</global-results>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<global-exception-mappings>
<!-- 该元素必须出现,可以出现无限多次-->
<exception-mapping name="" exception="" result="">
异常处理资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</exception-mapping>
</global-exception-mappings>
<action name="" class="" method="" converter="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
<!-- 下面元素可以出现0次,也可以无限多次 -->
<result name="" type="">
映射资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</result>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<interceptor-ref name="">
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</interceptor-ref>
<!-- 下面元素可以出现0次,也可以无限多次 -->
<exception-mapping name="" exception="" result="">
异常处理资源
<!-- 下面元素可以出现0次,也可以无限多次 -->
<param name="参数名">参数值</param>*
</exception-mapping>
</action>
</package>*
<struts>
3) default.properties
这个文件是struts2框架的全局属性文件,也是自动加载的文件。该文件包含了系列的key-value对。该文件完全可以配置在struts.xml文件中,使用constant元素。下面是这个文件中一些常见的配置项及说明。
### 指定加载struts2配置文件管理器,默认为org.apache.struts2.config.DefaultConfiguration
### 开发者可以自定义配置文件管理器,该类要实现Configuration接口,可以自动加载struts2配置文件。
# struts.configuration=org.apache.struts2.config.DefaultConfiguration
### 设置默认的locale和字符编码
# struts.locale=en_US
struts.i18n.encoding=UTF-8
### 指定struts的工厂类
# struts.objectFactory = spring
### 指定spring框架的装配模式
### 装配方式有: name, type, auto, and constructor (name 是默认装配模式)
struts.objectFactory.spring.autoWire = name
### 该属性指定整合spring时,是否对bean进行缓存,值为true or false,默认为true.
struts.objectFactory.spring.useClassCache = true
### 指定类型检查
#struts.objectTypeDeterminer = tiger
#struts.objectTypeDeterminer = notiger
### 该属性指定处理 MIME-type multipart/form-data,文件上传
# struts.multipart.parser=cos
# struts.multipart.parser=pell
struts.multipart.parser=jakarta
# 指定上传文件时的临时目录,默认使用 javax.servlet.context.tempdir
struts.multipart.saveDir=
struts.multipart.maxSize=2097152
### 加载自定义属性文件 (不要改写struts.properties!)
# struts.custom.properties=application,org/apache/struts2/extension/custom
### 指定请求url与action映射器,»˜认为org.apache.struts2.dispatcher.mapper.DefaultActionMapper
#struts.mapper.class=org.apache.struts2.dispatcher.mapper.DefaultActionMapper
### 指定action的后缀,默认为action
struts.action.extension=action
### 被 FilterDispatcher使用
### 如果为 true 则通过jar文件提供静态内容服务.
### 如果为 false 则静态内容必须位于 <context_path>/struts
struts.serve.static=true
### 被 FilterDispatcher使用
### 指定浏览器是否缓存静态内容,测试阶段设置为false,发布阶段设置为true.
struts.serve.static.browserCache=true
### 设置是否支持动态方法调用,true为支持,false不支持.
struts.enable.DynamicMethodInvocation = true
### 设置是否可以在action中使用斜线,默认为false不可以,想使用需设置为true.
struts.enable.SlashesInActionNames = false
### 是否允许使用表达式语法,默认为true.
struts.tag.altSyntax=true
### 设置当struts.xml文件改动时,是否重新加载.
### - struts.configuration.xml.reload = true
### 设置struts是否为开发模式,默认为false,测试阶段一般设为true.
struts.devMode = false
### 设置是否每次请求,都重新加载资源文件,默认值为false.
struts.i18n.reload=false
###标准的UI主题
### 默认的UI主题为xhtml,可以为simple,xhtml或ajax
struts.ui.theme=xhtml
###模板目录
struts.ui.templateDir=template
#设置模板类型. 可以为 ftl, vm, or jsp
struts.ui.templateSuffix=ftl
###定位velocity.properties 文件. 默认 velocity.properties
struts.velocity.configfile = velocity.properties
### 设置velocity的context.
struts.velocity.contexts =
### 定位toolbox.
struts.velocity.toolboxlocation=
### 指定web应用的端口.
struts.url.http.port = 80
### 指定加密端口
struts.url.https.port = 443
### 设置生成url时,是否包含参数.值可以为: none, get or all
struts.url.includeParams = get
### 设置要加载的国际化资源文件,以逗号分隔.
# struts.custom.i18n.resources=testmessages,testmessages2
### 对于一些web应用服务器不能处理HttpServletRequest.getParameterMap()
### 像 WebLogic, Orion, and OC4J等,须设置成true,默认为false.
struts.dispatcher.parametersWorkaround = false
### 指定freemarker管理器
#struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.FreemarkerManager
### 设置是否对freemarker的模板设置缓存
### 效果相当于把template拷贝到 WEB_APP/templates.
struts.freemarker.templatesCache=false
### 通常不需要修改此属性.
struts.freemarker.wrapper.altMap=true
### 指定xslt result是否使用样式表缓存.开发阶段设为true,发布阶段设为false.
struts.xslt.nocache=false
### 设置struts自动加载的文件列表.
struts.configuration.files=struts-default.xml,struts-plugin.xml,struts.xml
### 设定是否一直在最后一个slash之前的任何位置选定namespace.
struts.mapper.alwaysSelectFullNamespace=false
Struts2 Action的访问路径
Struts2 Action的访问路径
1. Action的访问路径
扩展名
缺省以.action结尾,请参考:default.properties文件,可以通过配置改变这一点:
<constant name="struts.action.extension" value="action,do,webwork" />
上述配置,将使得可以通过.action或.do或.webwork访问Action对象
访问路径的容错能力
比如:我们配置某个package的namespace="/simple",在这个package里面有一个action的
name是hello
那么,我们可以通过:
/simple/hello.action或
/simple/a/b/c/hello.action等等
都可以访问到这个action。
主要是在struts2中,对于访问的路径有一种容错能力,可以根据路径依次查找相应的package
中的action
配置action时如果不指定method属性
那么,用类似下面的方式:
http://localhost/struts2/simple/hello!say.action
可以调用hello这个action中的say方法
或者用下面的方式:
http://localhost/struts2/simple/hello.action?method:say=xxx
可以调用say方法,在这里,参数的名称是:method:say,这是最主要的,struts2正是
根据参数的名称来决定该调用哪个方法,而不是参数的值,所以参数的值可以是任意的
或者用下面的方法:
http://localhost/struts2/simple/hello.action
默认调用hello这个action中的execute方法!
struts.xml的配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="yy" namespace="/test" extends="struts-default">
<action name="helloWorld" class="com.struts2.study.yy.HelloWorldAction"
method="execute" >
<result name="success">/pages/helloWorld.jsp</result>
</action>
</package>
</struts>
当访问helloWorld时,我们当初的访问路径是:
http://localhost:8080/struts2study/test/helloWorld 此时能够正常访问。但是,当访问
http://localhost:8080/struts2study/test/test1/test2/test3/helloWorld 时也能正常访问,这就涉
及到struts2中Action名称的搜索路径问题,做如下解释:
(1)如果请求的URL路径为http://localhost:8080/struts2study/p1/p2/p3/helloWorld
首先寻找namesapce为/p1/p2/p3的package,如果不存在此package,则转向(2),如果存
在这个package,则在此package中寻找此action,如果找到了,则访问成功;如果没找到此
action,则转向默认namespace(namespace="" 或者不写)的package中去寻找,如果找到了
则正常访问,否则访问失败。
(2)寻找namespace为/p1/p2的package,如果不存在此package,则转向(3),如果存在
这个package,则在此package中寻找此action,如果找到了,则访问成功;如果没找到此
action,则转向默认namespace的package中去寻找,如果找到了则正常访问,否则访问失败
。
(3)寻找namespace为/p1的package,如果不存在此package,则转向(4),如果存在这个
package,则在此package中寻找此action,如果找到了,则访问成功;如果没找到此action,
则转向默认namespace的package中去寻找,如果找到了则正常访问,否则访问失败。
(4)寻找namesapce为/的package,如果不存在此package,则转向(5),如果存在这个
package,则在此package中寻找此action,如果找到了,则访问成功;如果没找到此action,
则转向默认namespace的package中去寻找,如果找到了则正常访问,否则访问失败。
(5)寻找默认的namespace的package,如果存在此package,寻找到相应的action,则访问
成功,否则访问失败。
下面给出对应的默认namespace的struts.xml配置:
<package name="yy2" namespace="" extends="struts-default">
<action name="helloWorld" class="com.struts2.study.yy.HelloWorldAction"
method="execute" >
<result name="success">/pages/helloWorld.jsp</result>
</action>
</package>
- [Struts2]Struts2学习总结
- 【Struts2框架】Struts2总结
- 【Struts2】Struts2要点总结
- struts2 总结
- Struts2总结
- struts2总结
- Struts2总结
- struts2总结
- struts2总结
- Struts2总结
- Struts2总结
- struts2 总结
- struts2总结
- struts2总结
- Struts2总结
- STRUTS2总结
- struts2 总结
- struts2总结
- 通过模板将数据导入到Word中
- doublewrite buffer
- file space
- rollback segment
- 自绘按钮的实现
- struts2总结
- ThinkPad E40上安装MAC OS
- 一位ALS患者在生命走到尽头前用脚贡献了最后一个代码补丁zt
- 亲爱的,谁不曾想岁岁年年?
- ONGL简介
- Flex 4 CSS Skin使用注意事项 skin组件制作概要
- 在bmp上写字
- java io 笔记二:FileOutPutStream、FileInPutStream、FileWriter、FileReader类
- 抽象类和接口的区别