解压apps目录下的struts2-blank.war:
仿照这个最基本的项目,拷贝相关文件:
1.拷贝apps/struts2-blank/WEB-INF/classes/struts.xml到我们的项目中的src目录(因为src编译完成后会自动放在classes目录);
2.拷贝类库apps/struts2-blank/WEB-INF/lib/*.jar到自己项目的WebRoot/WEB-INF/lib下。
3.拷贝apps/struts2-blank/WEB-INF/web.xml中关于过滤器的配置到我们的项目:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 6 <display-name></display-name> 7 8 <!-- 从struts2实例项目struts2-blank中拷贝的关于过滤器的配置 --> 9 <filter> 10 <filter-name>struts2</filter-name> 11 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 12 </filter> 13 <filter-mapping> 14 <filter-name>struts2</filter-name> 15 <url-pattern>/*</url-pattern> 16 </filter-mapping> 17 18 <welcome-file-list> 19 <welcome-file>index.jsp</welcome-file> 20 </welcome-file-list> 21 </web-app>
4.修改我们自己项目下的src/struts.xml,先将struts标记下的所有内容全部注释(方便以后参考),并配置我们自己的package:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 6 <struts> 7 8 <!-- 注释原来的配置 9 <constant name="struts.enable.DynamicMethodInvocation" value="false" /> 10 <constant name="struts.devMode" value="true" /> 11 12 <package name="default" namespace="/" extends="struts-default"> 13 14 <default-action-ref name="index" /> 15 16 <global-results> 17 <result name="error">/WEB-INF/jsp/error.jsp</result> 18 </global-results> 19 20 <global-exception-mappings> 21 <exception-mapping exception="java.lang.Exception" result="error"/> 22 </global-exception-mappings> 23 24 <action name="index"> 25 <result type="redirectAction"> 26 <param name="actionName">HelloWorld</param> 27 <param name="namespace">/example</param> 28 </result> 29 </action> 30 </package> 31 32 <include file="example.xml"/> 33 --> 34 <!-- Add packages here --> 35 36 <!-- 配置我们自己的package --> 37 <package name="default" namespace="/" extends="struts-default"> 38 <action name="hello"> 39 <result>/Hello.jsp</result> 40 </action> 41 </package> 42 43 </struts>
5.在WebRoot目录下新建一个Hello.jsp。
将项目部署到Tomcat进行访问。如果直接输入项目名称回车会出现以下错误:
HTTP Status 404 - There is no Action mapped for namespace [/] and action name [] associated with context path [/Struts2].
应该使用使用hello.action或者hello进行访问:
在上面的那个项目中如果我们在struts.xml中修改了action的名字:
1 <package name="default" namespace="/" extends="struts-default"> 2 <!-- 我们将action的名字由hello改成hello_struts由于不能立即生效,将出现404错误 --> 3 <action name="hello_struts"> 4 <result>/Hello.jsp</result> 5 </action> 6 </package>
在没有重启服务器的情况下将出现以下错误:
HTTP Status 404 - There is no Action mapped for namespace [/] and action name [hello_struts] associated with context path [/Struts2].
如果每次更改完struts.xml都要重启服务器就会比较麻烦。在struts.xml中配置一个开发模式的常量:
1 <constant name="struts.devMode" value="true" /> <!-- 开发模式,更改就有反馈 -->
这样就可以在MyEclipse中查看其源代码和使用帮助文档了。
Struts2把用户的请求和视图(展现给用户的页面)进行了分离。
namespace决定了action的访问路径,默认为空(""),可以接收所有的action,namespace可以写成/或者/xxx/yyy(namespace必须以斜杠开头),对应的action的访问路径为/index.action,或者/xxx/yyy/index.action。namespace最好也使用模块来进行命名(例如package的名字是user,那么namespace的名字也是/user)。result的名字如果是"success"则可以省略。
1 <!-- package用来解决重名的问题 --> 2 <package name="front" namespace="/front" extends="struts-default"> 3 <action name="index"> 4 <!-- result如果名字是success则可以省略 --> 5 <result name="success">/Namespace.jsp</result> 6 </action> 7 </package>
如果namespace为空,只要是以/index结尾的请求都可以访问:
1 <!-- 不写namespace与 namespace=""等价 --> 2 <package name="main" extends="struts-default"> 3 <action name="index"> 4 <result>/Namespace.jsp</result> 5 </action> 6 </package>
不同的package里面可以有相同的action,访问的时候是通过namespace进行查找,如果在对应的namespace下找到了action,就使用该action下的result配置;如果在指定的namespace下没有找到该action则找namespace为空的那个package的action,如果还是没有找到就直接报错。
例如有以下的的struts.xml:
1 <!-- 不同的package下有同名的action,一个package的namespace为/front,另一个为空 --> 2 <package name="front" namespace="/front" extends="struts-default"> 3 <action name="index"> 4 <result name="success">/Namespace.jsp</result> 5 </action> 6 </package> 7 8 <package name="main" extends="struts-default"> 9 <action name="index"> 10 <result>/Namespace1.jsp</result> 11 </action> 12 </package>
具体视图的返回可以由用户自定义的Action来决定。具体的手段是根据返回的字符串找到对应的设置项来决定视图的内容。具体Action的实现是一个普通的Java类,里面有public String execute()方法即可。继承ActionSupport,也可以实现Action接口。一般常用的是继承ActionSupport,好处是可以直接使用Struts2封装好的方法。
例如struts.xml中有以下配置:
1 <package name="front" namespace="/" extends="struts-default"> 2 <action name="index" class="org.gpf.struts2.front.action.IndexAction1"> 3 <result>/ActionIntroduction.jsp</result> 4 </action> 5 </package>
org.gpf.struts2.front.action.IndexAction1如下:
1 package org.gpf.struts2.front.action; 2 3 public class IndexAction1 { 4 5 public String execute(){ 6 7 return "success"; 8 } 9 }
Struts2和Struts1的区别在于Struts1每次访问的是同一个Action对象;Struts2每次new一个新的Action,不需要担心线程的同步问题。
上面的小程序的序列图如下:
当我们自己没有Action类的时候默认调用的是ActionSupport类中的execute()方法。查看ActionSupport类,发现它实现了Action接口,而Action接口中定义了一系列的常量和一个execute方法:
1 public class ActionSupport implements Action....{ 2 3 public String execute() throws Exception { 4 return SUCCESS; 5 } 6 7 } 8 9 public interface Action { 10 11 public static final String SUCCESS = "success"; 12 13 public static final String NONE = "none"; 14 15 public static final String ERROR = "error"; 16 17 public static final String INPUT = "input"; 18 19 public static final String LOGIN = "login"; 20 21 public String execute() throws Exception; 22 23 }
编写Action的3种方式(在实际开发中一般使用继承自ActionSupport):
1 package org.gpf.struts2.front.action; 2 3 /** 4 * 自定义一个类,有一个public String execute()方法 5 */ 6 public class IndexAction1 { 7 8 public String execute(){ 9 10 return "success"; 11 } 12 } 13 14 package org.gpf.struts2.front.action; 15 16 import com.opensymphony.xwork2.Action; 17 /** 18 * 实现Action接口 19 * @author gaopengfei 20 * @date 2015-5-9 下午5:47:00 21 */ 22 public class IndexAction2 implements Action { 23 24 @Override 25 public String execute() throws Exception { 26 27 return SUCCESS; 28 } 29 30 } 31 32 package org.gpf.struts2.front.action; 33 34 import com.opensymphony.xwork2.ActionSupport; 35 /** 36 * 继承ActionSupport 37 * @author gaopengfei 38 * @date 2015-5-9 下午5:46:48 39 */ 40 public class IndexAction3 extends ActionSupport { 41 42 @Override 43 public String execute() throws Exception { 44 45 return super.execute(); 46 } 47 }
有以下的struts配置:
1 <package name="path" namespace="/path" extends="struts-default"> 2 <action name="path" class="org.gpf.struts2.front.action.PathAction"> 3 <result name="path">/path.jsp</result> 4 </action> 5 </package>
有以下的PathAction:
1 package org.gpf.struts2.front.action; 2 3 public class PathAction { 4 5 public String execute(){ 6 7 return "path"; 8 } 9 }
我们通过以下的方式访问:
由于我们访问的时候没有指定namespace,所以会在web.xml中找到项目的默认欢迎页index.jsp。在index.jsp中有一个超链接:
<a href="path/path.action">路径问题说明</a>
下面的path.jsp与index.jsp都在网站的根目录:
1 <%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%> 2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 3 <html> 4 <head> 5 <title>path.jsp</title> 6 </head> 7 8 <body> 9 <h1>path.jsp</h1> 10 <a href="index.jsp">访问index.jsp将会出现404错误</a> 11 </body> 12 </html>
接下来我们进行以下的访问:
采用../的形式解决上述问题会非常麻烦。最好的方法是获取项目的绝对路径,所有的链接都使用绝对路径,在path.jsp的链接中进行如下修改:
1 <% 2 String path = request.getContextPath(); 3 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 4 %> 5 6 <a href="<%=path %>/index.jsp">使用request.getContextPath()解决路径问题</a>
或者在html的head标签中中增加一条base标签语句:
1 <%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%> 2 <% 3 String path = request.getContextPath(); 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 5 %> 6 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 7 <html> 8 <head> 9 <title>path.jsp</title> 10 <base href="<%=basePath%>" /> 11 <!-- 有了上面的这句话,所有的链接都会默认加上basepath --> 12 </head> 13 14 <body> 15 <h1>path.jsp</h1> 16 <a href="index.jsp">index.jsp</a> 17 </body> 18 </html>
Action执行的时候不一定要执行execute()方法。可以在配置文件中配置Action的时候用method=来指定执行哪个方法(产生太多的action),也可以在url地址中动态指定(DMI)【推荐】。
假设我们有以下的Action和Struts配置:
1 public class UserAction extends ActionSupport { 2 3 public String add() { 4 return SUCCESS; 5 } 6 7 }
1 <!-- 注意更改Struts2的常量值为允许DMI --> 2 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 3 <constant name="struts.devMode" value="true" /> 4 <package name="user" extends="struts-default" namespace="/user"> 5 <action name="user" class="org.gpf.struts2.user.action.UserAction"> 6 <result>/User_add_success.jsp</result> 7 </action> 8 <action name="Useradd" class="org.gpf.struts2.user.action.UserAction" method="add"> 9 <result>/User_add_success.jsp</result> 10 </action> 11 </package>
我们在访问UserAction的add方法的时候就有两种方式:①通过UserAdd这个Action的method属性指定需要访问的方法;二通过URL参数(!)动态指定。
当UserAction中新增加了一个方法的时候(例如delete方法,前者需要重新定义一个Userdelete的Action,后者只需要将!后面的参数改成delete即可)。
在配置struts的时候使用通配符可以极大地简化配置。见以下的例子:
struts.xml
1 <package name="actions" namespace="/actions" extends="struts-default"> 2 <!-- method中的{1}表示action的name属性中的第1个星号 --> 3 <action name="Student*" class="org.gpf.struts2.user.action.StudentAction" method="{1}"> 4 <result>/Student{1}_success.jsp</result> 5 </action> 6 </package>
StudentAction:
1 public class StudentAction extends ActionSupport { 2 3 public String add() { 4 return SUCCESS; 5 } 6 7 public String delete(){ 8 return SUCCESS; 9 } 10 }
在index.jsp中有2个链接:
1 <% 2 String path = request.getContextPath(); 3 %> 4 <a href="<%=path%>/actions/Studentadd">添加学生</a> 5 <a href="<%=path%>/actions/Studentdelete">删除学生</a>
当我们访问添加学生的时候,Studentadd这个Action与action标记中定义的name属性(Action*)相匹配,此时的*表示的就是add,从而该Action的method就是add,调用StudentAction的add方法,add方法返回"success",从而在result中显示Student_add_success.jsp(将result中的Student{1}_success.jsp中的{1}用add进行了替换)。
以上的通配符配置还可以进一步简化:
struts.xml
1 <package name="actions" namespace="/actions" extends="struts-default"> 2 <action name="*_*" class="org.gpf.struts2.user.action.{1}Action" method="{2}"> 3 <result>/{1}_{2}_success.jsp</result> 4 </action> 5 </package>
TeacherAction:
1 public class TeacherAction extends ActionSupport { 2 3 public String add() { 4 return SUCCESS; 5 } 6 7 public String delete(){ 8 return SUCCESS; 9 } 10 }
在struts.xml中我们不再配置具体的类名和方法名,都使用通配符表示。在上面的配置中action的name有两个*,action的class属性是{1},匹配第一个*,action的method属性匹配第二个*。这样一来就比较方便。我们可以在不改变struts.xml的情况下为TeacherAction添加其他的方法(例如update、query),还可以添加其他的Action,例如CourseAction(该Action的名字是*Action)。——采用以上这种方式可以动态地位Action添加方法,动态添加不同的Action(只需要实现约定好jsp页面、Action的命名规则,3行代码就可以搞定Action的配置)。
约定JSP页面大写字母开头,第一个下划线之前的内容表示的是Action名,第一个下划线之后是方法名(与指定的Action中的方法对应);Action的命名大写字母开头以Action结尾。
只要所有的开发人员都遵守以上的规则,struts的配置就非常容易。约定优于配置。
当action中的name可以匹配多个的时候,优先级是先具体再模糊匹配。即:没有通配符的先匹配。如果name中有星号则不管星号的多少(即1个星号和2个星号处于1个级别),这时他们的匹配规则取决于在struct.xml中的配置的先后次序。用以下的配置可以检测匹配顺序:
1 <package name="actions" namespace="/actions" extends="struts-default"> 2 3 <!-- 2个星号 --> 4 <action name="*_*" class="org.gpf.struts2.user.action.{1}Action" method="{2}"> 5 <result>/Teacher_2_star.jsp</result> 6 </action> 7 <!-- 没有通配符,精确匹配 --> 8 <action name="Teacher" class="org.gpf.struts2.user.action.StudentAction"> 9 <result>/Teacher_0_star.jsp</result> 10 </action> 11 <!-- 1个星号 --> 12 <action name="Teacher*" class="org.gpf.struts2.user.action.StudentAction" method="{1}"> 13 <result>/Teacher_1_star.jsp</result> 14 </action> 15 16 </package>
在index.jsp中我们通过这样的一个链接来访问(该链接同时匹配了多个Action)
1 <% 2 String path = request.getContextPath(); 3 %> 4 <a href="<%=path%>/actions/Teacher">老师</a>
总结:匹配规则:先精确匹配,后模糊匹配(一个星号和多个星号处于同一个匹配级别按照struts.xml中的先后顺序匹配)。
例如有以下的struts配置:
1 <!-- 注意更改Struts2的常量值为允许DMI --> 2 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 3 <constant name="struts.devMode" value="true" /> 4 <package name="user" extends="struts-default" namespace="/user"> 5 <action name="user" class="org.gpf.struts2.user.action.UserAction"> 6 <result>/info.jsp</result> 7 </action> 8 </package>
UserAction:
1 package org.gpf.struts2.user.action; 2 3 import com.opensymphony.xwork2.ActionSupport; 4 5 public class UserAction extends ActionSupport { 6 7 private String name; 8 private int age; 9 10 public String getName() { 11 return name; 12 } 13 14 public void setName(String name) { 15 this.name = name; 16 } 17 18 public int getAge() { 19 return age; 20 } 21 22 public void setAge(int age) { 23 this.age = age; 24 } 25 26 public String add() { 27 System.out.println("name = " + name); 28 System.out.println("age = " + age); 29 return SUCCESS; 30 } 31 32 }
在index.jsp中我们通过以下的方式访问action:
1 <% 2 String path = request.getContextPath(); 3 %> 4 使用action属性接收参数<a href="<%=path %>/user/user!add?name=张三&age=18">添加用户</a>
在index.jsp中我们使用DMI,先找到/user名称空间,然后找到名称为user的那个Action,并通过DMI调用其add方法。我们通过URL传递参数的时候将name和age传递给了该Action。(需要注意的是:在我们自己的Action中要定义和参数同名的封装属性,并写好它的getter和setter)。Action自动设置属性是通过setter,其中setXXX中的XXX就是参数的名。也就是说我们的Action可以写成这样:
1 public class UserAction extends ActionSupport { 2 3 private String username; 4 5 public String getName() { 6 return username; 7 } 8 9 /** 10 * 属性不一定要和参数匹配,但是setXXX中的XXX一定要和参数匹配 11 */ 12 public void setName(String name) { 13 this.username = name; 14 } 15 16 17 public String add() { 18 System.out.println("name = " + username); 19 return SUCCESS; 20 } 21 22 }
域模型就是在问题域中真正存在的实体的概念,例如一个BBS中的域模型就有:板块、话题、注册用户等。
域模型User:
1 package org.gpf.struts2.user.model; 2 3 public class User { 4 5 private String name; 6 private int age; 7 private String sex; 8 9 public String getSex() { 10 System.out.println("getSex<--" + sex); 11 return sex; 12 } 13 14 public void setSex(String sex) { 15 System.out.println("setSex-->" + sex); 16 this.sex = sex; 17 } 18 19 public String getName() { 20 System.out.println("getName<--" + name); 21 return name; 22 } 23 24 public void setName(String name) { 25 System.out.println("setName-->" + name); 26 this.name = name; 27 } 28 29 public int getAge() { 30 System.out.println("getAge<--" + age); 31 return age; 32 } 33 34 public void setAge(int age) { 35 System.out.println("setAge-->" + age); 36 this.age = age; 37 } 38 39 }
UserAction:
1 package org.gpf.struts2.user.action; 2 3 import org.gpf.struts2.user.model.User; 4 5 import com.opensymphony.xwork2.ActionSupport; 6 7 public class UserAction extends ActionSupport { 8 9 private User user; // 声明一个User域模型,Struts2会自动帮我们new 10 11 public String getInfo() { 12 System.out.println("域模型传递参数name:"+user.getName()); 13 System.out.println("域模型传递参数age:"+user.getAge()); 14 System.out.println("域模型传递参数sex:"+user.getSex()); 15 return SUCCESS; 16 } 17 18 // 提供访问器 19 public User getUser() { 20 System.out.println("getUser"); 21 return user; 22 } 23 24 public void setUser(User user) { 25 System.out.println("setUser"); 26 this.user = user; 27 } 28 29 }
Struts2配置:
1 <!-- 注意更改Struts2的常量值为允许DMI --> 2 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 3 <constant name="struts.devMode" value="true" /> 4 <package name="user" extends="struts-default" namespace="/user"> 5 <action name="user" class="org.gpf.struts2.user.action.UserAction"> 6 <result>/info.jsp</result> 7 </action> 8 </package>
我们通过以下的链接传递参数:
<a href="<%=path%>/user/user!getInfo?user.name=张三&user.age=12&sex=男">使用DomainModel接收参数</a>
注意:Action中持有与域模型User的一个引用,并提供对该引用的getter和setter。我们不需要对User对象进行初始化,Struts会默认new出一个对象,在进行URL传递参数的时候应该采用域模型对象.属性的方式。其中域对象的名字和Action中持有的引用名一致。
其他地方无需改动,将我们的UserAction实现一个ModelDriven<T>接口:
1 package org.gpf.struts2.user.action; 2 3 import org.gpf.struts2.user.model.User; 4 5 import com.opensymphony.xwork2.ActionSupport; 6 import com.opensymphony.xwork2.ModelDriven; 7 8 public class UserAction extends ActionSupport implements ModelDriven<User> { 9 10 private User user = new User(); // 这里的User必须自己new! 11 12 public String getInfo() { 13 14 System.out.println("name参数:" + user.getName()); 15 System.out.println("age参数:" + user.getAge()); 16 System.out.println("sex参数:" + user.getSex()); 17 return SUCCESS; 18 } 19 20 @Override 21 public User getModel() { 22 23 System.out.println("getModel"); 24 return user; 25 } 26 27 }
Struts2中的MVC。V就是jsp页面、M是后台的那些模型、C就是Action,C控制着V和M之间的通信(V和M进行了解耦)。
小技巧:struts.xml中的常量配置可以在/org/apache/struts2/default.properties中找到。
在UserAction中我们定义两个属性来接收用户名和密码参数。在本程序中如果用户名和密码都是admin则验证通过,跳转到Login_success.jsp,否则跳转到Login_failure.jsp。struts的配置:
1 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 2 <constant name="struts.devMode" value="true" /> 3 <package name="user" extends="struts-default" namespace="/user"> 4 <action name="user" class="org.gpf.struts2.user.action.UserAction"> 5 <result>/Login_success.jsp</result> <!-- 成功转发至成功页 --> 6 <result name="error">/Login_failure.jsp</result> <!-- 失败转发至失败页 --> 7 </action> 8 </package>
1 public class UserAction extends ActionSupport{ 2 3 private String username; // 两个属性接收用户名和密码参数 4 private String password; 5 6 public String getUsername() { 7 return username; 8 } 9 10 public void setUsername(String username) { 11 this.username = username; 12 } 13 14 public String getPassword() { 15 return password; 16 } 17 18 public void setPassword(String password) { 19 this.password = password; 20 } 21 22 public String check(){ 23 24 if(!"admin".equals(username) || !"admin".equals(password)){ 25 this.addFieldError("errors", "错误的用户名或者密码!"); 26 return ERROR; 27 } 28 return SUCCESS; 29 } 30 }
在UserAction中我们通过addFieldError()方法将错误信息保存了起来,在前台页面可以使用struts2提供的标签库(<s:fielderror>)将错误信息显示出来:
PS:J2EE5之后不需要将标签的tld文件放在自己的WEB-INF目录,只要jar包中有会自动搜索。
1 登录失败! 2 <%@taglib prefix="s" uri="/struts-tags"%> 3 <s:fielderror fieldName="errors"></s:fielderror><br />
以上的标签给我们强加了一个ul>li和errorMessage的CSS类,用户定制很差,我们可以使用<s:debug>在页面上打印Struts 的Value Stack(各种各样的错误),使用<s:property>标签可以取出Value Stack Contents和Stack Context(其实就是Action Context)中的属性。在Login_failure.jsp中我们这样写:
1 登录失败!<br /> 2 <%@taglib prefix="s" uri="/struts-tags"%> 3 <s:property value="errors.errors[0]"/> <!-- OGNL表达式 --> 4 <s:debug></s:debug>
取出了错误信息我们就可以自己进行样式的设置了。
其实主要就是使用Action取得Map类型的request、session、application以及真实类型HttpServletRequest、HttpSession、ServletContext。因为我们的结果是通过result返回出去的,所以但对于response我们可以不管。
index.jsp
1 取得Map类型request,session,application,真实类型 HttpServletRequest,HttpSession, ServletContext的引用: 2 <ol> 3 <li>前三者:依赖于容器</li> 4 <li>前三者:IOC</li> (只用这种) 5 <li>后三者:依赖于容器</li> 6 <li>后三者:IOC</li> 7 </ol> 8 <br /> 9 <form name="f" action="" method="post"> 10 用户名:<input type="text" name="name" /> 11 密码:<input type="text" name="password" /> <br /> 12 <input type="button" value="submit1" 13 onclick="javascript:document.f.action='login/login1';document.f.submit();" /> 14 <input type="button" value="submit2" 15 onclick="javascript:document.f.action='login/login2';document.f.submit();" /> 16 <input type="button" value="submit3" 17 onclick="javascript:document.f.action='login/login3';document.f.submit();" /> 18 <input type="button" value="submit4" 19 onclick="javascript:document.f.action='login/login4';document.f.submit();" /> 20 </form>
在index.jsp中我们通过javascript代码动态指定表单提交的Action。这样我们就可以通过多个按钮提交同一个表单。struts.xml中Action的配置如下(通过通配符动态指定):
1 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 2 <constant name="struts.devMode" value="true" /> 3 <package name="login" extends="struts-default" namespace="/login"> 4 <action name="login*" class="org.gpf.struts2.user.action.LoginAction{1}"> 5 <result>/Login_success.jsp</result> 6 </action> 7 </package>
LoginAction1.java
1 public class LoginAction1 extends ActionSupport{ 2 3 private Map request; 4 private Map session; 5 private Map application; 6 7 /** 8 * 通过ActionContext.getContext()方法 9 * 在构造器中为request、session和application初始化 10 */ 11 public LoginAction1() { 12 request = (Map) ActionContext.getContext().get("request"); 13 session = ActionContext.getContext().getSession(); 14 application = ActionContext.getContext().getApplication(); 15 } 16 17 public String execute(){ 18 19 // 向request、session和application的Map中设置内容 20 request.put("r1", "r1"); 21 session.put("s1", "s1"); 22 application.put("a1", "a1"); 23 return SUCCESS; 24 } 25 }
在Login_success.jsp中我们将request、session、application范围内设置的属性取出:
1 登录成功! 2 <%@taglib prefix="s" uri="/struts-tags"%> 3 <s:debug></s:debug> 4 <table> 5 <tr> 6 <th>通过ValueStack取得Web元素中的内容</th> 7 <th>通过3种属性范围取得Web元素中的内容</th> 8 </tr> 9 <tr> 10 <td>取得request</td> 11 <td><s:property value="#request.r1"/></td> 12 <td><%=request.getAttribute("r1") %></td> 13 </tr> 14 <tr> 15 <td>取得session</td> 16 <td><s:property value="#session.s1"/></td> 17 <td><%=session.getAttribute("s1") %></td> 18 </tr> 19 <tr> 20 <td>取得application</td> 21 <td><s:property value="#application.a1"/></td> 22 <td><%=application.getAttribute("a1") %></td> 23 </tr> 24 </table>
通过debug标记,我们可以发现在StackContext(也就是ActionContext)中存在request、session、application3个键,而我们为其设置的值r1、s1和a1则保存在它们的Value中(Value也是Map)。所以我们可以在LoginAction1中通过ActionContext.getContext().get("request")等方法获取对应的Map集合。
除了可以使用#session.属性名、#request.属性名、#application.属性名访问我们设置在这3种属性范围之内的属性以外,我们还可以使用#attr.属性名的方式自动搜索匹配属性名的字段:
1 <s:property value="#attr.r1"/> 2 <s:property value="#attr.s1"/> 3 <s:property value="#attr.a1"/>
但是开发中这3种属性范围之内的属性应该是精确定位的,不应该依靠自动搜索范围是从request到application(因为可能有重名属性)。
该种方式需要实现XXXaware接口。aware的意思是得知、获得。
1 /** 2 * LoginAction2实现了RequestAware,SessionAware,ApplicationAware接口 3 */ 4 public class LoginAction2 extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{ 5 6 private Map<String, Object> request; // 依赖外界的环境把值注给我们,而不是我们自己主动拿 7 private Map<String, Object> session; 8 private Map<String, Object> application; 9 10 public String execute(){ 11 request.put("r1", "r1"); 12 session.put("s1", "s1"); 13 application.put("a1", "a1"); 14 return SUCCESS; 15 } 16 17 @Override 18 public void setApplication(Map<String, Object> application) { 19 20 this.application = application; 21 } 22 23 @Override 24 public void setSession(Map<String, Object> session) { 25 26 this.session = session; 27 } 28 29 @Override 30 public void setRequest(Map<String, Object> request) { 31 32 this.request = request; 33 } 34 35 }
IoC(Inverse of Control,控制反转)也叫做DI(dependency injection,依赖注入)。
所谓依赖注入就是我们在LoginAction2中的3个属性依赖Struts2将值传给我们而不是我们自己主动取得值。所谓控制反转就是原先我们需要自己控制3个属性的值(获取),而现在3个属性的值交给Struts2容器进行。以上的2个概念在Spring中大量应用——我们自己定义的成员变量从来不初始化而是交给Spring进行初始化,Spring是通过配置文件的方式进行的初始化。
前面获取的方式都是获取的Map,如果想要获得HttpServletRequest、HttpSession、ServletContext可以使用下面的方法:
1 public class LoginAction3 extends ActionSupport { 2 3 private HttpServletRequest request; 4 private HttpSession session; 5 private ServletContext application; 6 7 public LoginAction3() { 8 9 request = ServletActionContext.getRequest(); 10 session = request.getSession(); 11 application = ServletActionContext.getServletContext(); 12 } 13 14 public String execute(){ 15 16 request.setAttribute("r", "r"); 17 session.setAttribute("s", "s"); 18 application.setAttribute("a", "a"); 19 return SUCCESS; 20 } 21 22 }
1 public class LoginAction4 extends ActionSupport implements ServletRequestAware { 2 3 private HttpServletRequest request; 4 private HttpSession session; 5 private ServletContext application; 6 7 @Override 8 public void setServletRequest(HttpServletRequest request) { 9 10 this.request = request; 11 this.session = request.getSession(); 12 this.application = request.getServletContext(); 13 } 14 15 public String execute(){ 16 17 request.setAttribute("r", "r"); 18 session.setAttribute("s", "s"); 19 application.setAttribute("a", "a"); 20 return SUCCESS; 21 } 22 }
一般来说struts.xml中配置的是所有struts的公共配置,将具体的业务划分成模块。例如:登录模块、退出模块。
struts.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 6 <struts> 7 <!-- 公共配置 --> 8 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 9 <constant name="struts.devMode" value="true" /> 10 11 <!-- 以下是模块的划分 --> 12 <include file="login.xml"></include> 13 <include file="logout.xml"></include> 14 15 </struts>
login.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="login" extends="struts-default" namespace="/login"> <action name="login*" class="org.gpf.struts2.user.action.LoginAction{1}"> <result>/Login_success.jsp</result> </action> </package> </struts>
logout.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 <struts> 6 <package name="logout" extends="struts-default" namespace="/loginlogout"> 7 <action name="logout*" class="org.gpf.struts2.user.action.LogoutAction{1}"> 8 <result>/Logout_success.jsp</result> 9 </action> 10 </package> 11 </struts>
此种方式可以进行模块的划分,项目组的各个成员各自开发,最后在struts.xml中进行所有模块的整合。
当访问的Action不存在的时候我们可以通过以下的方式交给默认的Action处理:
1 <package name="default" extends="struts-default" namespace="/"> 2 <!-- 当找不到Action的时候跳到默认的Action --> 3 <default-action-ref name="index"></default-action-ref> 4 <action name="index"> 5 <result>/default.jsp</result> 6 </action> 7 </package>
例如:在浏览网站的时候有时候出现的404错误就可以交给默认Action处理,默认Action可以跳转到网站的主页提高用户体验。