项目文件结构
Struts2应用的分层体系架构
要点:
1.将处理逻辑放到service里,如Action层不准许出现在sql语句,session、request不允许传到service层去。
示例:
LoginAction.java
package com.struts2.struts2;
……该导入的包……
public class LoginAction extends ActionSupport
{
private String username;
private String password;
private int age;
private Date date;
private LoginService loginService = new LoginServiceImpl();
……Set与get方法……
public String execute() throws Exception
{ //业务逻辑不写到Action里
// if(!"hello".equals(username))
// {
// throw new UsernameException("username invalid");
// }
// if(!"world".equals(password))
// {
// throw new PasswordException("password invalid");
// }
// return SUCCESS;
//利用ServletActionContext,获得HttpServletRequest请求
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
session.setAttribute("hello", "helloworld");
ActionContext actionContext = ActionContext.getContext();
Map<String, Object> map = actionContext.getSession(); //获取session
Object object = map.get("hello"); //打印出session的信息
System.out.println(object);
if(this.loginService.isLogin(username, password))
{
return SUCCESS;
}
return INPUT;
}
………………
}
LoginService.java
package com.struts2.service;
public interface LoginService
{
public boolean isLogin(String username, String password);
}
LoginServiceImpl.java
package com.struts2.service.impl;
import com.struts2.service.LoginService;
public class LoginServiceImpl implements LoginService
{
public boolean isLogin(String username, String password)
{
if("hello".equals(username) && "world".equals(password))
{
return true;
}
return false;
}
}
编辑struts2.xml文件
…………
<action name="login" class="com.struts2.struts2.LoginAction">
<result name="success" type="dispatcher">/result.jsp</result>
<result name="input">/login.jsp</result>
</action>
…………
Struts2的模型驱动(model driven)
要点:
- 属性驱动(propertity driven)与模型驱动的区别:之前的都是属性驱动,属性驱动与模型驱动的处理方式差不多,但它们导致的结果却不同,属性驱动是在接收的请求后,将对象里的值一个一个的 set 好,此时并没有new 出一个整的对象。而模型驱动是在接收到用户提交表单请求后,就将对象创建好了,并把对象的值也设定好了,可以直接拿对象来用了。
- 实现了ModelDriven接口的Action的执行过程
1).首先调用getModel()方法,返回一个person对象
2).模型查找提交过来的表单中有没有username、password等属性名是否匹配,如果匹配,则将对应的值自动设置到对象中,放到模型当中。
3).当在结果页面,以request.getAttribute()或requestScope取值时,同样通过Action从模型当中取值。
- 属性驱动与模型驱动的比较
1)属性驱动灵活,准确;模型驱动不灵活,因为很多时候,页面所提交过来的参数并不属于模型中的属性,也就是说页面所提交过来的参数与模型中的属性并不一致,这是很常见的情况。
2)模型驱动更加符合面向对象的编程风格,使得我们获得的是对象而不是一个个离散的值。
小结:推荐使用属性驱动编写 Action。
- Preparable接口的作用是让 Action完成一些初始化工作,这些初始化工作是放在 Preparable接口的prepare方法中完成的,该方法会在execute方法执行之前得到调用。
示例:
Person.java
package com.struts2.bean;
import java.util.Date;
public class Person
{
private String username;
private String password;
private int age;
private Date date;
……get、set方法……
}
LoginAction2.java
package com.struts2.struts2;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.struts2.bean.Person;
public class LoginAction2 extends ActionSupport implements ModelDriven<Person>, Preparable
{ //模型驱动
private Person person = new Person();
//1.首先调用getModel()方法,返回一个person对象
//2.模型查找提交过来的表单中有没有username、password等属性值,如果有,则模型自动设置到对象中
public Person getModel()
{
System.out.println("getModel invoked!");
return person; //返回生成的person
}
//调用这个方法让action准备自身
public void prepare() throws Exception
{
System.out.println("prepare invoked!!");
}
public String execute() throws Exception
{
System.out.println("execute invoked!");
//System.out.println(person.getUsername());
return SUCCESS;
}
}
struts.xml
向struts.xml文件中,添加一个action,如下:
<action name="login2" class="com.struts2.struts2.LoginAction2">
<result name="success">/result.jsp</result>
username: <input type="text" name="username"><br>
password: <input type="password" name="password"><br>
age: <input type="text" name="age"><br>
date: <input type="text" name="date"><br>
<input type="submit" value="submit">
username: ${requestScope.username }<br><!-- requestScope相当于request.getAttribute() -->
password: ${requestScope.password }<br>
date: ${requestScope.date }<br>
session: ${sessionScope.hello }<!-- 取得通过ServletActionContext方式获取的session -->
2) Mock 测试 ( 继承 HttpServletRequest 、 HttpSession 、HttpServletResponse等Servlet API) 。
打开struts2-core-2.2.1.1.jar下的struts-default.xml文件,其中定义了的结果类型,如下:
<package name="struts-default" abstract="true">
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<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="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" />
在struts.xml文件中,设置Action中的result元素的type属性即可,如下:
<action name="login" class="com.struts2.struts2.LoginAction">
<result name="success" type="dispatcher">/result.jsp</result>
<result name="success" type="redirect">/result.jsp</result>
<result name="input">/login.jsp</result>
<form action="action1.action">
username: <input type="text" name="username"><br>
password: <input type="password" name="password"><br>
<input type="submit" value="submit">
username: <s:property value="username"/><br>
password: <s:property value="password"/><br>
usernameAndPassword: <s:property value="usernameAndPassword"/>
import com.opensymphony.xwork2.ActionSupport;
public class Action1 extends ActionSupport
private String usernameAndPassword;
public String execute() throws Exception
this.usernameAndPassword = this.username + this.password;
import com.opensymphony.xwork2.ActionSupport;
public class Action2 extends ActionSupport
private String usernameAndPassword;
public String execute() throws Exception
<action name="action1" class="com.struts2.struts2.Action1">
<result name="success" type="redirectAction">
<!--如果不传参数,可把action2直接写到result的text域中 -->
<param name="actionName">action2</param>
<param name="username">${username}</param><!-- 获取Action1的成员变量 -->
<param name="password">${password}</param>
<param name="usernameAndPassword">${usernameAndPassword}</param>
<action name="action2" class="com.struts2.struts2.Action2">
<result name="success">/action2.jsp</result>
要点:直接修改struts.xml文件中的action的result的type属性的值为 chain ,如下:
<action name="action1" class="com.vvvv.struts2.Action1">
<result name="success" type=chain">
<!--如果不传参数,可把action2直接写到result的text域中 -->
<param name="actionName">action2</param>
<param name="username">${username}</param><!-- 获取Action1的成员变量 -->
<param name="password">${password}</param>
<param name="usernameAndPassword">${usernameAndPassword}</param>
<s:form action="token.action" theme="simple">
username: <s:textfield name="username"></s:textfield><br>
password: <s:password name="password"></s:password><br>
<s:submit value="submit"></s:submit>
import com.opensymphony.xwork2.ActionSupport;
public class TokenAction extends ActionSupport
public String execute() throws Exception
username: <s:property value="username"/><br>
password: <s:property value="password"/>
<action name="token" class="com.struts2.struts2.TokenAction">
<result name="success">/tokenSuccess.jsp</result>
<result name="invalid.token">/tokenFail.jsp</result>
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
package com.struts2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class TheInterceptor1 implements Interceptor
public void setTest(String test)
System.out.println("setTest invoked!"); //set方法先于init方法执行
System.out.println("init invoked!");
System.out.println("test: " + this.test);
//服务器永远不会去执行挂截器,只不过是在执行拦截器之前,执行一个invoke方法
public String intercept(ActionInvocation invocation) throws Exception
String result = invocation.invoke();
<package name="struts2" extends="struts-default">
<interceptor name="theInterceptor1" class="com.vvvv.interceptor.TheInterceptor1">
<param name="test">vvvv</param>
<action name="token" class="com.struts2.struts2.TokenAction">
<result name="success">/tokenSuccess.jsp</result>
<result name="invalid.token">/tokenFail.jsp</result>
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
一旦定义了自己的拦截器,将其配置到 action上后,我们需要在action的最后加上默认的拦截器栈:defaultStack。
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TheInterceptor2 extends AbstractInterceptor
public String intercept(ActionInvocation invocation) throws Exception
System.out.println("interceptor before...");
System.out.println("interceptor2: " + invocation.getAction().getClass());
String result = invocation.invoke();
System.out.println("interceptor after...");
<interceptor name="theInterceptor1" class="com.vvvv.interceptor.TheInterceptor1">
<param name="test">vvvv</param>
<interceptor name="theInterceptor2" class="com.vvvv.interceptor.TheInterceptor2">
<interceptor name="theInterceptor3" class="com.vvvv.interceptor.TheInterceptor3">
<interceptor name="loginInterceptor" class="com.vvvv.interceptor.LoginInterceptor">
<action name="action1" class="com.vvvv.struts2.Action1" method="myExecute">
<result name="success" type="chain">
<param name="actionName">action2</param>
<param name="username">${username}</param>
<param name="password">${password}</param>
<param name="usernameAndPassword">${usernameAndPassword}</param>
<interceptor-ref name="theInterceptor1"></interceptor-ref>
<interceptor-ref name="theInterceptor2"></interceptor-ref>
<interceptor-ref name="theInterceptor3">
<param name="includeMethods">execute, myExecute</param>
<param name="excludeMethods">myExecute</param>
<interceptor-ref name="defaultStack"></interceptor-ref>
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.vvvv.listener.TheListener;
public class TheInterceptor3 extends MethodFilterInterceptor
protected String doIntercept(ActionInvocation invocation) throws Exception
invocation.addPreResultListener(new TheListener());
System.out.println("before interceptor3");
String result = invocation.invoke();
System.out.println("after interceptor3");
<interceptor name="theInterceptor1" class="com.vvvv.interceptor.TheInterceptor1">
<param name="test">vvvv</param>
<interceptor name="theInterceptor2" class="com.vvvv.interceptor.TheInterceptor2">
<interceptor name="theInterceptor3" class="com.vvvv.interceptor.TheInterceptor3">
<interceptor name="loginInterceptor" class="com.vvvv.interceptor.LoginInterceptor">
<interceptor-stack name="myDefaultInterceptorStack">
<interceptor-ref name="loginInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<default-interceptor-ref name="myDefaultInterceptorStack"></default-interceptor-ref>
<result name="usernameInvalid">/usernameInvalid.jsp</result>
<result name="passwordInvalid">/passwordInvalid.jsp</result>
<result name="login">/error.jsp</result>
<action name="login" class="com.vvvv.struts2.LoginAction">
<result name="success" type="dispatcher">/result.jsp</result>
<result name="input">/login.jsp</result>
<action name="login2" class="com.vvvv.struts2.LoginAction2">
<result name="success">/result.jsp</result>
<action name="userAction" class="com.vvvv.struts2.UserAction">
<result name="success">/output.jsp</result>
<action name="userAction2" class="com.vvvv.struts2.UserAction2">
<result name="success">/output.jsp</result>
<action name="register" class="com.vvvv.struts2.RegisterAction" method="myExecute">
<result name="success">/registerResult.jsp</result>
<result name="input">/register.jsp</result>
<action name="action1" class="com.vvvv.struts2.Action1" method="myExecute">
<result name="success" type="chain">
<param name="actionName">action2</param>
<param name="username">${username}</param>
<param name="password">${password}</param>
<param name="usernameAndPassword">${usernameAndPassword}</param>
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.vvvv.struts2.LoginAction;
public class LoginInterceptor extends AbstractInterceptor
@SuppressWarnings("unchecked")
public String intercept(ActionInvocation invocation) throws Exception
if(LoginAction.class == invocation.getAction().getClass())
Map map = invocation.getInvocationContext().getSession();
if(null == map.get("userInfo"))
同时,也可以在一个Struts.xml中有多个package ,例如,可以将前台的Action放到一个package里,将后台的一个package放到另一个包里面。
<include file="xxx.xml"></include>
struts-2.3.1-all.zip\struts-2.3.1\apps\struts2-mailreader.war\WEB-INF\src\java\struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<constant name="struts.action.extension" value="do" />
<constant name="struts.devMode" value="false" />
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<include file="mailreader-default.xml"/>
<include file="mailreader-support.xml"/>
<package name="hello" extends="struts-default" namespace="/theNamespace">
<action name="namespaceAction" class="com.vvvv.struts2.NamespaceAction">
<result name="success">output.jsp</result>
import com.opensymphony.xwork2.ActionSupport;
public class NamespaceAction extends ActionSupport
public String execute() throws Exception
2)Action中所定义的File类型的成员变量file实际上指向的是临时目录中的临时文件,然后在服务器端通过 IO的方式将临时文件写入到指定的服务器端目录中。
<form action="fileUpload.action" method="post" enctype="multipart/form-data">
username: <input type="text" name="username"><br>
file: <input type="file" name="file"><br>
<input type="submit" value="submit">
username: <s:property value="username"/><br>
name:<s:property value="fileFileName"/>
type:<s:property value="fileContentType"/>
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
//DiskFileItemFactory创建在内存中保存内容的FileItem实例
DiskFileItemFactory factory = new DiskFileItemFactory();
String path = req.getRealPath("/upload");
factory.setRepository(new File(path));
factory.setSizeThreshold(1024 * 1024);
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> list = (List<FileItem>)upload.parseRequest(req);
String name = item.getFieldName();
//如果是普通文本域返回true; 如果不是普通文本域返回false.
String value = item.getString();
System.out.println(name + "=" + value);
req.setAttribute(name, value);
String value = item.getName();
int start = value.lastIndexOf("\\");
String fileName = value.substring(start + 1);
req.setAttribute(name, fileName);
item.write(new File(path, fileName));
// OutputStream os = new FileOutputStream(new File(path, fileName));
// InputStream is = item.getInputStream();
// byte[] buffer = new byte[400];
// while((length = is.read(buffer)) != -1)
// os.write(buffer, 0, length);
req.getRequestDispatcher("fileUploadResult.jsp").forward(req, resp);
WEB-INF/lib/struts2-core-2.2.1.1.jar/org.apache.struts2/default.properties
### This can be used to set your default locale and encoding scheme
struts.i18n.encoding=UTF-8 //指定了国际化的默认编码格式
### if specified, the default object factory can be overridden here
### Note: short-hand notation is supported in some cases, such as "spring"
### Alternatively, you can provide a com.opensymphony.xwork2.ObjectFactory subclass name here
# struts.objectFactory = spring
### specifies the autoWiring logic when using the SpringObjectFactory.
### valid values are: name, type, auto, and constructor (name is the default)
struts.objectFactory.spring.autoWire = name
### Used by the DefaultActionMapper
### You may provide a comma separated list, e.g. struts.action.extension=action,jnlp,do
### The blank extension allows you to match directory listings as well as pure action names
### without interfering with static resources.
struts.action.extension=action,, //指定了表单提交的文件的后缀名
<form action="fileUpload.action" method="post" enctype="multipart/form-data">
username: <input type="text" name="username"><br>
file: <input type="file" name="file"><br>
<input type="submit" value="submit">
username: <s:property value="username"/><br>
name:<s:property value="fileFileName"/>
type:<s:property value="fileContentType"/>
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UploadAction extends ActionSupport
private String fileContentType;
public String execute() throws Exception
//文件上传的步骤是:先将上传的文件缓存在struts.multipart.saveDir=xxx指定的目录下,
//然后从xxx目录下,通过输出流将文件上传到服务器指定的目录("/upload")
String root = ServletActionContext.getRequest().getRealPath("/upload");
InputStream is = new FileInputStream(file);
System.out.println("path: " + file.getAbsolutePath());
System.out.println("file: " + file.getName());
System.out.println("fileFileName: " + fileFileName);
File destFile = new File(root, fileFileName);
OutputStream os = new FileOutputStream(destFile);
byte[] buffer = new byte[400];
while(-1 != (length = is.read(buffer)))
struts.action.extension=action
struts.multipart.saveDir=c:/vvvv
struts.multipart.maxSize=1048576000
注:限定上传文件的大小等也可以在struts.xml文件中配置,如下:
<constant name="struts.multipart.maxSize" value="5242880"></constant>
<form action="fileUpload2.action" method="post" enctype="multipart/form-data">
username: <input type="text" name="username"><br>
file: <input type="file" name="file"><br>
file2: <input type="file" name="file"><br>
file3: <input type="file" name="file"><br>
<input type="submit" value="submit">
username: <s:property value="username"/><br>
<s:iterator value="fileFileName" id="f">
file: <s:property value="#f.toUpperCase()"/><br><!-- ognl表达示 -->
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UploadAction2 extends ActionSupport
private List<String> fileFileName;
private List<String> fileContentType;
public String execute() throws Exception
for(int i = 0; i < file.size(); i++)
InputStream is = new FileInputStream(file.get(i));
String root = ServletActionContext.getRequest().getRealPath("/upload");
File destFile = new File(root, fileFileName.get(i));
OutputStream os = new FileOutputStream(destFile);
byte[] buffer = new byte[400];
while(-1 != (length = is.read(buffer)))
<action name="fileUpload2" class="com.vvvv.struts2.UploadAction2">
<result name="success">/fileUploadResult2.jsp</result>
<action name="downloadFile2" class="com.vvvv.struts2.DownloadAction2">
<result type="stream"><!-- 动态的获得文件的名字 -->
<param name="contentDisposition">attachment;filename=${filename}</param>
<param name="inputName">downloadFile</param>
<a href="downloadFile2.action?number=1">下载文件</a>
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownloadAction2 extends ActionSupport
public InputStream getDownloadFile()
this.filename = new String(this.filename.getBytes("gbk"),
return ServletActionContext.getServletContext()
.getResourceAsStream("/upload/中文.txt");
this.filename = "CaptureSprite.exe";
return ServletActionContext.getServletContext()
.getResourceAsStream("/upload/CaptureSprite.exe");
public String execute() throws Exception
@ParentPackage("struts-default")
@Action(value = "login", results = {
@Result(name = "success", location = "/login.jsp"),
@Result(name = "input", location = "/login.jsp") })
//@InterceptorRef("defaultStack")
//@InterceptorRefs({@InterceptorRef(""), @InterceptorRef("")})
//@ExceptionMappings({@ExceptionMapping(),@E.....})
public class LoginAction extends ActionSupport
private LoginService loginService = new LoginServi
public class GetXMLAction extends ActionSupport
public void setName(String name)
public String execute() throws Exception
People people1 = new People();
people1.setAddress("beijing");
People people2 = new People();
people2.setAddress("tianjin");
Document document = DocumentHelper.createDocument();
Element rootElement = document.addElement("persons");
rootElement.addComment("This is comment!!");
Element e = rootElement.addElement("person");
Element idElement = e.addElement("id");
Element nameElement = e.addElement("name");
Element ageElement = e.addElement("age");
Element addressElement = e.addElement("address");
idElement.setText(people1.getId() + "");
nameElement.setText(people1.getName());
ageElement.setText(people1.getAge() + "");
addressElement.setText(people1.getAddress());
idElement.setText(people2.getId() + "");
nameElement.setText(people2.getName());
ageElement.setText(people2.getAge() + "");
addressElement.setText(people2.getAddress());
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("text/xml; charset=utf-8");
response.setHeader("cache-control", "no-cache");
PrintWriter out = response.getWriter();
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(out, format);
<script type="text/javascript" src="scripts/jquery-1.4.4.js"></script>
<script type="text/javascript">
$("#button1").click(function()
$.post("getJsonAction2.action",{name: $("#name").val()},
function(returnedData, status)
$("#theBody table:eq(0)").remove();
<option value="zhangsan">zhangsan</option>
<option value="lisi">lisi</option>
<input type="button" value="get json content from server" id="button1">
public class GetJsonAction extends ActionSupport
public void setName(String name)
public String execute() throws Exception
String result = gson.toJson(people);
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("application/json; charset=utf-8");
response.setHeader("cache-control", "no-cache");
PrintWriter out = response.getWriter();
GetJsonAction2.java(利用struts2的插件)
public class GetJsonAction2 extends ActionSupport