1.将struts2过滤器添加到web.xml中
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<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>
</web-app>
2. 配置struts2的配置文件(在src目录中创建struts.xml文件)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
</package>
</struts>
3创建action(action就是一个POJO类)
3.1、为action编写execute方法
package com.yan.struts.action;
public class HelloAction {
public String execute(){
System.out.println("hello struts");
return "success";
}
}
3.2、在struts.xml文件中配置action和返回结果集
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<!--在url中输入hello,会找到相应的class去执行(默认执行execute方法),执行完返回一个字段(success),在result中匹配从而确定要跳转到哪个页面-->
<action name="hello" class="com.yan.struts.action.HelloAction">
<result name="success">/hello.jsp</result>
</action>
</package>
</struts>
默认执行execute方法,但有多个操作显然不够,所以可以进行指定执行哪个方法
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<!-- url输入user_addInput执行UserAction中的addInput方法,根据返回值确定跳转的页面 -->
<action name="User_addInput" class="com.yan.struts.action.UserAction" method="addInput">
<result name="success">/WEB-INF/user/addInput.jsp</result>
</action>
<action name="User_add" class="com.yan.struts.action.UserAction" method="add">
<result name="success">/WEB-INF/user/add.jsp</result>
</action>
<action name="User_list" class="com.yan.struts.action.UserAction" method="list">
<result name="success">/WEB-INF/user/list.jsp</result>
</action>
</package>
</struts>
package com.yan.struts.action;
public class UserAction {
public String addInput(){
System.out.println("addInput");
return "success";
}
public String add(){
System.out.println("add");
return "success";
}
public String list(){
System.out.println("list");
return "success";
}
}
上述配置使得可以在一个action类中写多个方法,不用受execute影响建多个action类,但在配置中依旧需要配置多个action
package com.yan.struts.action;
public class UserAction {
public String addInput(){
System.out.println("addInput");
return "success";
}
public String add(){
System.out.println("add");
return "success";
}
public String list(){
System.out.println("list");
return "success";
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="User" class="com.yan.struts.action.UserAction">
<result name="add">/WEB-INF/User/add.jsp</result>
<result name="addInput">/WEB-INF/User/addInput.jsp</result>
<result name="list">/WEB-INF/User/list.jsp</result>
</action>
</package>
</struts>
用上述配置就可以在调用时确定调用的方法,以"(actionname)!(要调用的方法名)"为url,如要调用UserAction中的add方法,可以使用User!add,还有一种通过(actionname)?method:(要调用的方法名)访问。
这些方法依旧要做大量的返回值配置,还有一种通配符配置
package com.yan.struts.action;
public class UserAction {
public String addInput(){
System.out.println("addInput");
return "success";
}
public String add(){
System.out.println("add");
return "success";
}
public String list(){
System.out.println("list");
return "success";
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="*_*" class="com.yan.struts.action.{1}Action" method="{2}">
<result>/WEB-INF/{1}/{2}.jsp</result>
</action>
</package>
</struts>
url输入User_add时,{1}为User,{2}为add,即调用UserAction中的add方法,页面跳转到User文件夹下的add.jsp目录。这样的好处显而易见,如果还有其他的Action类,以actionname_方法名也适用,自动调用,省去一大批配置,当然文件夹名和jsp名要按照一定约束命名。
----------------------------------------------------------------------------------------------
传值的方式(指的是action传给视图的方式,而form给action只要action有set get方法,直接传,action可以直接获取):
方式一:通过属性。
package com.yan.struts.action;
public class UserAction {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String addInput(){
System.out.println("addInput");
return "success";
}
public String add(){
System.out.println("add");
return "success";
}
public String list(){
this.setName("张三");
this.setAge(20);
System.out.println("list");
return "success";
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>User list</h1>
${name}----${age }
</body>
</html>
或者struts有自己的标签库
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>User list</h1>
<s:property value="name"/>----<s:property value="age"/>
</body>
</html>
方式二:通过actionContext传值,其实就是存进一个Map
public String list(){
ActionContext.getContext().put("aaa","张三");
ActionContext.getContext().put("bbb", "123");
System.out.println("list");
return "success";
}
获取
<s:property value="#aaa"/>----<s:property value="#bbb"/>
访问actionContext中的数据都需要加#,不过如果存的是字符串,struts2.3以后可以不用#号
方式三:通过servlet的API传值
public String list(){
ServletActionContext.getRequest().setAttribute("hello", "world");
System.out.println("list");
return "success";
}
获取
<s:property value="#request.hello"/>
获取值的方式又不一样,如果是#hello是获取不到,什么原因?我们需要学一下ognl和ValueStack,要理解根本
--------------------------------------------------------------------------------------------------
package com.yan.struts.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import com.yan.struts.model.Department;
import com.yan.struts.model.Role;
import com.yan.struts.model.User;
import ognl.Ognl;
import ognl.OgnlException;
public class TestOgnl {
@Test
public void test01() {
try {
User u=new User(1,"ts","唐三");
//第二个参数为root,意思是输出u的id值
System.out.println(Ognl.getValue("id", u));
Department de=new Department("财务部");
u.setDe(de);
System.out.println(Ognl.getValue("de.name", u));
} catch (OgnlException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void test02() {
try {
Map<String,Object > ctx=new HashMap<String,Object>();
User u=new User(1,"ts","唐三");
Department de=new Department("财务部");
Role r=new Role(1,"管理员");
ctx.put("user", u);
ctx.put("role", r);
u.setDe(de);
//在根root即u中找username
System.out.println(Ognl.getValue("username",ctx, u));
//加#号在map即ctx中找所存的user的username
System.out.println(Ognl.getValue("#user.username",ctx, u));
//目前root是ctx所以也可以取到
System.out.println(Ognl.getValue("role.name", ctx,ctx));
//Ognl其实就是一个大的Context(Map),根的key就是root,值为u
System.out.println(Ognl.getValue("#root.username",ctx,u));
} catch (OgnlException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void test03() {
try {
List<User>users=new ArrayList<User>();
users.add(new User(1,"张三","zs"));
users.add(new User(2,"王五","ww"));
users.add(new User(3,"赵六","zl"));
//root为list要用#root[index]方式访问
System.out.println(Ognl.getValue("#root[0].username",users));
} catch (OgnlException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void test04() {
try {
User u=new User();
//可以调用方法
System.out.println(Ognl.getValue("add(1,2)", u));
System.out.println(Ognl.getValue("hello('world')", u));
//在test03还可以
System.out.println(Ognl.getValue("get(1).username",users));
} catch (OgnlException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.yan.struts.model;
public class User {
private int id;
public Department de;
private String username;
private String nickname;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Department getDe() {
return de;
}
public void setDe(Department de) {
this.de = de;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public User(int id, String username, String nickname) {
super();
this.id = id;
this.username = username;
this.nickname = nickname;
}
public User() {
super();
}
public int add(int a,int b){
return a+b;
}
public String hello(String para){
return "hello"+para;
}
}
package com.yan.struts.model;
public class Department {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department(String name) {
super();
this.name = name;
}
}
package com.yan.struts.model;
public class Role {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Role(int id, String name) {
super();
this.id = id;
this.name = name;
}
}
对于struts,所有值都存在ValueStack中,ValueStack包括ActionContext(其实是个Map)和CompoundRoot(使用list的栈结构),调用一个action时将它进栈,当使用compoundRoot时使用#root[0]表示从第一个元素(栈顶)取数据(不加如username也是取第一个元素的username),取不到就取下一个元素的username,以此类推。
现在回过头可以理解获取方式一传值的时候直接value="username"就可以,因为他是从root中取值(TestOgnl里的test01),而获取方式二所传的值要用#aaa,因为是存在actionContext(Map)里的(test02),同理方式三,actionContext里存request。
可以往compoundRoot添加一个元素自己做做测试
现在回过头可以理解获取方式一传值的时候直接value="username"就可以,因为他是从root中取值(TestOgnl里的test01),而获取方式二所传的值要用#aaa,因为是存在actionContext(Map)里的(test02),同理方式三,actionContext里存request。
可以往compoundRoot添加一个元素自己做做测试
UserAction中的list方法
this.setName("八戒");
this.setAge(123);
User u=new User(10,"孙悟空","swn");
ActionContext.getContext().getValueStack().push(u);
此时栈中存了UserAction对象和对象u,可以自己测试#root[0].XXX会获取哪个元素
<s:debug/>
可以在页面查看compoundRoot和actionContext,里面的都可以用对应的方法取出
----------------------------------标签--------------------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>User list</h1>
<s:if test="age>18">成年人</s:if>
<s:else>未成年</s:else>
<!--用迭代会把users中元素依次放在CompoundRoot,完了就出栈-->
<!--当定义了var后,会将元素在ActionContext中也存一份-->
<s:iterator value="#users">
<s:property value="id"/>-------<s:property value="username"/>
</s:iterator>
<br/>
<s:iterator value="#users" var="u">
<s:property value="#u.username"/>
</s:iterator>
<s:debug/>
</body>
</html>
public String list(){
List<User>users=new ArrayList<User>();
users.add(new User(1,"张三","zs"));
users.add(new User(2,"王五","ww"));
users.add(new User(3,"赵六","zl"));
ActionContext.getContext().put("users", users);
return "success";
}
设置status是在ActionContext中放一份
-------------------------------------------------------------------------------------------------------------
当一个类有太多属性,比如User有username还有邮箱地址等许多属性,在UserAction中一个一个写出来要写太多东西,很冗余,而且set和get方法就更多了,因此我们可以将User一整个作为属性。如MessageAction中:
package com.yan.struts.action;
import com.yan.struts.model.Message;
public class MessageAction {
private Message msg;
public Message getMsg() {
return msg;
}
public void setMsg(Message msg) {
this.msg = msg;
}
public String addInput(){
return "success";
}
public String add(){
return "success";
}
}
package com.yan.struts.model;
public class Message {
private int id;
private String title;
private String content;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
这样就简化很多,表单提交时自动映射到对应的值,就相当于form传值给action
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Message addInput</h1>
<form action="Message_add.action">
id:<input type="text" name="msg.id"/><br/>
title:<input type="text" name="msg.title"/><br/>
content:<input type="text" name="msg.content"><br>
<input type="submit"/>
</form>
</body>
</html>
提交后的url可以看出传值自动映射了
http://localhost:8080/Struts01/Message_add.action?msg.id=3&msg.title=fff&msg.content=eee
可以看到表单的name值都要加上msg.,有点繁琐,我们可以有更好的方法,让MessageAction实现ModelDriver接口
package com.yan.struts.action;
import com.opensymphony.xwork2.ModelDriven;
import com.yan.struts.model.Message;
public class MessageAction implements ModelDriven<Message>{
private Message msg;
public Message getMsg() {
return msg;
}
public void setMsg(Message msg) {
this.msg = msg;
}
public String addInput(){
return "success";
}
public String add(){
return "success";
}
@Override
public Message getModel() {
if(msg==null)msg=new Message();
return msg;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Message addInput</h1>
<form action="Message_add.action">
id:<input type="text" name="id"/><br/>
title:<input type="text" name="title"/><br/>
content:<input type="text" name="content"><br>
<input type="submit"/>
</form>
</body>
</html>
原理:实现ModelDriver接口重写getModel方法后,会将对应的Model对象push到root的栈中
此时name为msg.id(MessageAction中)或id(Message)都可以
--------------------------------------------------------类型转化器--------------------------------------------------
如果MessageAction有一个point属性,我们想在文本框输入x,y形式设置point的值是做不到的,我们需要利用转换器
package com.yan.struts.action;
import java.util.Date;
import com.opensymphony.xwork2.ModelDriven;
import com.yan.struts.model.Message;
import com.yan.struts.model.Point;
public class MessageAction implements ModelDriven<Message>{
private Message msg;
private Point point;
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public Message getMsg() {
return msg;
}
public void setMsg(Message msg) {
this.msg = msg;
}
public String addInput(){
return "success";
}
public String add(){
return "success";
}
@Override
public Message getModel() {
if(msg==null)msg=new Message();
return msg;
}
}
package com.yan.struts.model;
public class Point {
private String x;
private String y;
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public String getY() {
return y;
}
public void setY(String y) {
this.y = y;
}
}
package com.yan.struts.converter;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import com.yan.struts.model.Point;
public class PointConverter extends StrutsTypeConverter {
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
Point p=null;
if(values.length<=1){
String str=values[0];
String []codes=str.split(",");
p=new Point();
p.setX(codes[0]);
p.setY(codes[1]);
}
return p;
}
@Override
public String convertToString(Map context, Object o) {
Point p=(Point)o;
return p.getX()+","+p.getY();
}
}
可以设置全局转化器,创建xwork-conversion.properties文件
com.yan.struts.model.Point=com.yan.struts.converter.PointConverter
可以设置局部转化器,创建MessageAction-conversion.properties文件
point=com.yan.struts.converter.PointConverter
获取值${point}无法获取,要用<s:property value="point"/>
---------------------------------------------文件上传----------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="Message_file.action" method="post" enctype="multipart/form-data">
<input type="file" name="photo"/>
<input type="submit"/>
</form>
</body>
</html>
package com.yan.struts.action;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import com.opensymphony.xwork2.ModelDriven;
import com.yan.struts.model.Message;
import com.yan.struts.model.Point;
public class MessageAction implements ModelDriven<Message>{
private Message msg;
private Point point;
private Date endTime;
private File photo;
private String photoFileName;
private String photoContentType;
public String getPhotoFileName() {
return photoFileName;
}
public void setPhotoFileName(String photoFileName) {
this.photoFileName = photoFileName;
}
public String getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String photoContentType) {
this.photoContentType = photoContentType;
}
public File getPhoto() {
return photo;
}
public void setPhoto(File photo) {
this.photo = photo;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public Message getMsg() {
return msg;
}
public void setMsg(Message msg) {
this.msg = msg;
}
public String addInput(){
return "success";
}
public String add(){
return "success";
}
@Override
public Message getModel() {
if(msg==null)msg=new Message();
return msg;
}
public String fileInput(){
return "success";
}
public String file(){
try {
System.out.println(photoFileName+"..."+photoContentType);
FileUtils.copyFile(photo, new File("d:/"+photoFileName));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "success";
}
}
文件上传的大小可以通过设置类似
<constant name="struts.multipart.maxSize" value="10240000"></constant>
其他属性也可以设置
----------------------------------------拦截器-----------------------------------------------------
----------------------------------------拦截器-----------------------------------------------------
首先创建类继承AbstractInterceptor
package com.yan.struts.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class HelloInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("hello interception");
return invocation.invoke();
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<!-- 创建拦截器的配置 -->
<interceptors>
<interceptor name="helloInterceptor" class="com.yan.struts.interceptor.HelloInterceptor"></interceptor>
</interceptors>
<action name="*_*" class="com.yan.struts.action.{1}Action" method="{2}">
<!-- 在action中添加拦截器 -->
<interceptor-ref name="helloInterceptor"></interceptor-ref>
<result>/WEB-INF/{1}/{2}.jsp</result>
</action>
</package>
</struts>
上述的配置有问题,因为继承了struts-default。默认使用了defaultStack,而这个栈包含了许多拦截器。但在action添加了自己的拦截器就不在默认使用,少了那些拦截器会让程序出现问题。我们可以进行如下修改:
手动添加defaultStack
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<!-- 创建拦截器的配置 -->
<interceptors>
<interceptor name="helloInterceptor" class="com.yan.struts.interceptor.HelloInterceptor"></interceptor>
</interceptors>
<action name="*_*" class="com.yan.struts.action.{1}Action" method="{2}">
<!-- 在action中添加拦截器 -->
<interceptor-ref name="helloInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result>/WEB-INF/{1}/{2}.jsp</result>
</action>
</package>
</struts>
或者自己创建一个栈包含defaultStack,然后在action中引入自定义栈
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<!-- 创建拦截器的配置 -->
<interceptors>
<interceptor name="helloInterceptor" class="com.yan.struts.interceptor.HelloInterceptor"></interceptor>
<interceptor-stack name="helloStack">
<interceptor-ref name="helloInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="*_*" class="com.yan.struts.action.{1}Action" method="{2}">
<!-- 在action中添加拦截器 -->
<interceptor-ref name="helloStack"></interceptor-ref>
<result>/WEB-INF/{1}/{2}.jsp</result>
</action>
</package>
</struts>