一. Action实现类
1. Action
Struts2的核心功能是action,对于开发人员来说,使用struts2框架,主要的编码工作就是编写Action类。
Struts2的Action类就是普通POJO。
Action类的属性通常是用来接收请求参数和返回页面数据的。
属性需要设置setXxx方法和getXxx方法,分别用来设置和获取属性的值。
最普通的Action类需要一个public String execute() throws Exception,该方法返回一个逻辑字符串。
为了规范用户开发Action类,Struts2框架提供了一个Action接口,该接口中提供了Action的通用规范模范。
该接口com.opensymphony.xwork2.Action的定义:
public interface Action{
public static final string SUCCESS="success";
public static final string NONE="none";
public static final string ERROR="error";
public static final string INPUT="input";
public static final string LOGIN="login";
public String execute() throws Exception;
}
该接口定义的5个常量的意义:
SUCCESS | 表示动作执行成功,并应该把相应的结果视图显示给用户。 |
NONE | 表示动作执行,但不应该把任何结果视图显示给用户。 |
ERROR | 表示动作执行不成功,并应该把相应的报错视图显示给用户。 |
INPUT | 表示输入验证失败,并应该把用户输入的那个表单重新显示给用户。 |
LOGIN | 表示动作没有执行(用户没有登录),并应该把登录视图显示给用户。 |
当然,Struts2 Action类不一定要实现Action接口,也可以编写一个普通的Java类作为Action,只要该类提供一个返回String的无参的public方法,如下所示:
public String xxx()
在实际开发中,Action类很少直接实现Action接口,通常都是从com.opensymphony.xwork2.ActionSupport类继承。ActionSupport实现了Action接口和Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable的接口,提供了输入验证、错误信息存取,以及国际化的支持,选择从ActionSupport继承,可以简化action的开发。
一下是ActionSupport类的部分内容:
public class ActionSupport implments Action,Validateable,ValidateableAware,TextProvider,LocaleProvider,Serializable{
public void addActionError(String anErrorMessage){}
public void addActionMessage(String aMessage){}
public void addFieldError(String fieldName,String errorMessage){}
public void input(){}
public void execute(){}
public void validate(){}
public void getText(String aTextName){}
}
在struts2框架中每一个Action是一个工作单元。Action负责将一个请求对应到一个Action的处理上去,当一个Action类和一个请求相匹配,该Action类的处理方法就会被Struts2框架调用。
2. 间接访问Servlet API(与Servlet API解耦)(访问Map类型)
(1)非IoC方式(不常用)
Struts2框架中,Action与Servlet API相分离,这种低耦合Action不方便访问Session,ServletContext等对象。
Struts2框架提供ActionContext类。通过该类可以访问Servlet API。
ActionContext是Action的上下文对象。其中保持了action执行所需的一组对象,包括parameters,request,session,application和locale等。
ActionContext类定义了如下方法,用于获取HttpServlet,HttpSession和ServletContext对应的Map对象。
ActionContext类的常用方法:
方法名称 | 说明 |
static ActionContext getContext() | 获得当前线程的ActionContext对象。 |
Map getApplication() | 获取封装了ServletContext的Map对象。 |
Map getSession() | 获取封装了HttpSession的Map对象。 |
Map getParameters() | 返回一个包含所有HttpServletRequest参数信息的Map对象。类似于HttpServletRequest的getParameterMap()方法。 |
Object get(Object key) | 通过key来寻找当前ActionContext中的值。相当于getAttributes方法。 |
void put(Object key,Object value) | 向当前ActionContext对象中存入键值对信息。相当于setAttributes方法。 |
void setApplication(Map application) | 设置ServletContext的值。 |
void setSession(Map session) | 设置Session的值。 |
(2)IoC方式(最推荐使用该方法)
接口名称 | 说明 |
RequestAware |
实现该接口方法 void setRequest(java.util.Map map) Action中需要有一个private Map对象。
|
SessionAware | 实现该接口方法 void setSession(java.util.Map map) Action中需要有一个private Map对象。 |
ApplicationAware |
实现该接口方法 void setApplication(java.util.Map map) Action中需要有一个private Map对象。
|
3. 直接访问Servlet API(与Servlet API耦合)(访问真实类型)
(1)非IoC方式(不常用)
Struts2提供org.apache.struts2.ServletActionContext类来获得Servlet API。在ServletActionContext类中有以下静态方法,可以直接得到Response,ServletContext,Response对象:
public static HttpServletRequest getRequest()
public static ServletContext getServletContext()
public static HttpServletResponse getResponse()
通过ServletActionContext定义的常量和ActionContext的get方法也可以直接得到相应的Servlet对象。
ActionContext.getContext().get(ServletActionContext.HTTP_REQUEST) 得到HttpServletRequest对象。
ActionContext.getContext().get(ServletActionContext.SERVLET_CONTEXT) 得到ServletContext对象。
ActionContext.getContext().get(ServletActionContext.HTTP_RESPONSE) 得到HttpServletResponse对象。
(2)IoC方式(不常用)
通过IoC方式访问Servlet API,就必须在Action中实现相应的接口,这些接口是:
接口名称 | 说明 |
ServletContextAware | 实现该接口的Action可以直接访问ServletContext对象。 Action需要实现该接口的方法:void setServletContext(javax.servlet.ServletContext servletContext) Action中需要有一个private ServletContext对象。 |
ServletRequestAware | 实现该接口的Action可以直接访问HttpServletRequest对象。 Action需要实现该接口的方法:void setServletRequest(javax.servlet.HttpServletRequest srequest) Action中需要有一个private HttpServletRequest对象。 |
ServletResponseAware | 实现该接口的Action可以直接访问HttpServletResponse对象。 Action需要实现该接口的方法:void setServletResponse(javax.servlet.HttpServletResponse response) Action中需要有一个private HttpServletResponse对象。 |
SessionAware | 实现该接口的Action可以直接访问HttpSession对象。 Action需要实现该接口的方法:void setSession(java.util.Map map) Action中需要有一个private Map对象。 |
二. Action配置
1. 基本配置
开发Action类后,就需要配置action映射,以告诉struts2框架,针对某个URL的请求应该对应哪个Action类进行处理。Struts.xml中的每一个action元素就是一个action映射。
action完整的属性如表所示:
属性 | 是否必须 | 说明 |
name | 是 | action的名字,匹配请求URI |
class | 否 | Action类的完整类名 |
method | 否 | 执行Action时调用的方法 |
converter | 否 | 应用于action的类型转换器的完整类名 |
例:
<action name="login" class="com.xxx.action.LoginAction"> <result>/success.jsp</result> <result name="error">/error.jsp</result> </action>
说明:
(1)Action的name属性必需,其他属性可选。默认情况下在name属性不允许出现斜杠(/)的。
如果需要在名字中使用斜杠(/),则需要配置struts2的struts.enable.SlashesInActionNames属性,来打开这个功能。
例:<action name="com/manager" class="com.xxx.BookManager" />
在struts.xml文件配置片段:
<constant name="struts.enable.SlashesInActionNames" value="true" />
(2)在为action取名时,名字中尽量不要使用点号(.)(例如create.user)和连接字符(-)(例如:my-action),否则会出现一些莫名其妙的问题。
其实我们不用去记忆名字中不允许出现哪些字符,只要按照通常的命名习惯去为action取名就可以了,顶多在名字中使用一下下划线(_)(例如:my_action),这样是可以保证不会出现问题。
2. 使用method属性
在软件开发中,经常会用到CRUD操作,CRUD即create(创建)、Read/Retrieve(读取)、Update(更新)和Delete(删除)的首字母的缩写。例如:在一个用户管理系统中,对用户有四种操作:添加用户、浏览用户、编辑用户和删除用户,分别对应CRUD的四种操作。在具体实现时,为了节省action类的数量,通常是在一个action类中编写四个方法来实现CRUD操作。
public String loginUser() throws Exception{} //查询用户
public String addUser() throws Exception{} //添加用户
public String updateUser() throws Exception{} //修改用户
public String deleteUser() throws Exception{} // 删除用户
在执行action时,默认调用方法是execute()。
在配置action时,通过action元素的method属性来指定处理方法。所指定的方法必须遵循与execute方法相同的格式。
在struts.xml文件中,对同一个action类配置不同的别名,并使用method属性,如下所示:
<action name="reg" class="UserAction" method="addUser"> <result name="success" type="redirect">index.jsp</result> <result name="input">reg.jsp</result> </action> <action name="login" class="UserAction" method="loginUser"> <result name="success">index.jsp</result> <result name="input">reg.jsp</result> </action> <action name="update" class="UserAction" method="updateUser"> <result name="success">index.jsp</result> <result name="input">reg.jsp</result> </action> <action name="delete" class="UserAction" method="deleteUser"> <result name="success">index.jsp</result> <result name="input">reg.jsp</result> </action>
使用method属性的注意点
Struts2在根据action元素的method属性查找方法时有两种途径:
(1)查找与method属性值完全一致的方法
(2)查找doMethod()形式的方法。
例,
当请求reg时,struts2首先查找addUser()方法,如果找不到,则继续查找doAddUser() 方法。这种特性是从WebWork继承而来的,而WebWork提供这种特性,一方面是为了兼容旧版本WebWork遗留的功能,另一方面是因为一些方法别名的需要,例如,default是Java的关键字,不能作为方法名,你可以使用doDefault作为方法名,在配置时,可以使用method=“default”.
这种特性同样也适用于动态方法调用,例如,请求/users! addUser.action,struts2会依次查找addUser()和doAddUser()方法。
3. Action动态方法调用
动态方法调用(Dynamic Method Invocation,DMI)可以不要任何配置,而直接调用Action中的非execute()方法。
在<action>的action属性中使用感叹号( ! )来标识要调用的方法名,请求URI或action属性值的格式为:
Action名称!方法名称.action
或
Action名称!方法名称
例:配置了如下的Action
<action name=“users” class=“com.xxx.UserAction” >…..</action>
当请求/users!addUser.action时,将调用UserAction的addUser()方法;
当请求/users!updateUser.action时,将调用UserAction的updateUser()方法;
DMI的实现本质是,其实现代码扫描action名字中的“!”字符,如果找到,则“欺骗”框架来调用“!”字符后的方法。
虽然调用的是其它方法,但是它使用的仍然是execute方法的配置,包括验证。框架“相信”它正在调用的是Action类的execute()方法。
在struts2中使用DMI有两个要注意的问题:
首先,DMI可能会带来安全隐患,由于通过URL可以执行action中的任意方法,恶意的用户会利用这一特性来执行一些敏感的方法,即使你不想这些方法被执行。所以在确定使用DMI时,你应该确保Action中的方法都是普通、开放的方法;否则,你应该加上安全控制。
其次,DMI与通配符方法有所重叠。
由于上述两个原因,struts2提供了一个属性配置,用于关闭DMI。
struts.xml的配置片段:
<constant name="struts.enable.DynamicMethodInvocation" value="false">
method属性配置与动态方法调用的使用情况:
a. 如果同一个Action的不同方法使用的是相同的配置(同样的拦截器引用,同样的result映射),那么可以使用动态方法调用。
b. 如果不同的方法调用需要配置不同的拦截器引用、不同的result映射,那么可以使用action元素的method属性,为同一个Action配置多个别名。
4. Action 通配符映射
有时Action中方法的调用满足一定的规律。则可以适当使用通配符以减少配置代码。
例:
<action name="*Crud" class=“com.xxx.Crud" method="{1}">
则editCrud.Action的引用将调用edit方法,deleteCrud.Action的引用将调用delete 方法。
{1}表示匹配第一个*。
通配符即星号(*),用于匹配0个或多个字符。在配置action时,可以在action元素的name属性中使用星号(*)来匹配任意的字符。
通配符中几个特殊的标记,如下所述:
*:可以匹配0个或多个任意字符,但是不包括正斜杠”/”;
**:可以匹配0个或多个任意字符,包括正斜杠”/”在内
\:反斜杠,转义字符。
因为*不能匹配正斜杠,所以前面/editSubscription/add不会被匹配上。
在action mapping中,被通配符所匹配的值可以用符号{N}来访问,其中N是1到9的数,用来表示代替第N个被匹配上的值,说通俗点就是第N个*号所表示的内容。
例:
通配符表达式是/edit*/add*/confirm*,它匹配上了/editOrder/addItem/confirmResult。则
{1}就代表Order,{2}就代表Item,{3}就代表Result。
Struts2对通配符的检索规则:
如果配置文件中包含多种通配符的使用,Struts2按照如下规则检索Action。
(1)如果能够找到name属性值与请求的Action名完全一致的<action>时,则匹配该<action>元素,而不会匹配使用通配符的配置。
(2)如果没有找到完全一致的匹配时,则按照匹配文件的匹配顺序依次匹配。最先符合的<action>将会匹配。所以<action name="*">的形式放在最后面,以防止这种形式被最先匹配。
三. 默认Action
Struts2框架允许使用默认Action。
在struts.xml中配置:
<package name="default" extends="struts-default"> <default-action-ref name="DefaultAction" /> 指定默认Action的名称为DefaultAction <action name="defaultaction" class="DefaultAction"> 配置DefaultAction <result name="success">/default.jsp</result> </action> </package>
默认Action的作用:当用户请求找不到对应的<action>元素时,系统将调用默认Action来接收用户请求。
说明:若没有默认Action,则当请求不存在action时,struts2会出现错误。
应用:实现美化的404页面,在访问不存在请求时直接跳转到首页。等。