struts2中关于action的使用说明

struts2中关于action的使用说明


   2012-03-29 20:31 星期四
  对于Struts2应用中的开发者来说,Action才是整个应用系统的核心,开发者需要提供大量的Action类,并且在struts.xml文件中对Action类进行配置,Action中包含了对用户请求的处理逻辑,因此,我们也称Action为业务控制器.
  
  1) 实现Action实现类
  
  相对于Struts1而言,Struts2采用的低侵入式的设计,Struts2不要求Action类继承任何的Struts2基类,或者实现任何的Struts2的接口,在这种设计方式下,Struts2的Action类是一个非常普通的POJO(但是通常会包含一个没有参数的execute()方法),从而,有很好的代码复用性.
  
  Struts2通常直接使用Action来封装HTTP请求参数,因此,Action类里面还应该包含与请求参数相对应的属性,并且为这些属性提供getter和setter方法.
  
  比如说,用户请求包含user和password两个请求参数,那么Action就应该提供user和两个属性来封装用户的请求参数,并且为user和password提供对应的getter和setter方法,西面是处理该请求的Action的代码片段:
  
  package com.supermos.app.Action;
  
   
  
  //处理用户请求的Action类,这个只是一个POJO类
  
  //不需要继承任何的基类,不需要实现任何的接口
  
  public class LoginAction {
  
   //提供两个属性来封装HTTP请求的参数
  
   private String user;
  
   private String password;
  
   public String getUser() {
  
   return user;
  
   }
  
   public void setUser(String user) {
  
   this.user = user;
  
   }
  
   public String getPassword() {
  
   return password;
  
   }
  
   public void setPassword(String password) {
  
   this.password = password;
  
   }
  
   //Action类默认来处理用户请求的方法,execute
  
   public String execute(){
  
   //处理结束,返回一个字符串,这个字符串将对应视图物理资源
  
   return "success";
  
   }
  
  }
  
  上面的Action类只是一个普通的Java类,这个Java类提供了两个属性,user和password,这个两个属性分别对应两个HTTP请求参数.
  
  (即使Action需要处理的请求包含user和password两个HTTP请求参数,Action类也是可以不包含user和password两个属性的,因为系统是通过对应的getter和setter方法来处理请求参数的,而不是通过属性的名字来处理请求参数的,也就是说,如果包含user的HTTP请求参数,Action类里面是否包含user属性并不重要,重要的是要包含void setUser(String user)和String getUser()这两个方法)
  
  Action类的属性,不仅可以用于封装请求参数,还可以用于封装处理结果.比如在前面的Action代码中看到的,如果希望将服务器提示的”登录成功”在下一个页面中输出,那么,我们可以在Action类中增加一个tip属性,并且为该属性提供对应的getter和setter方法,也就是为Action类增加如下的代码片段:
  
  //封装服务器提示的tip属性
  
   private String tip;
  
   public String getTip() {
  
   return tip;
  
   }
  
   public void setTip(String tip) {
  
   this.tip = tip;
  
   }
  
  一旦在Action中设置了tip属性的值,我们就可以在下一个页面当中使用struts2标签来输出该属性的值,在jSP页面当中输出tip属性值的代码片段如下:
  
  
  
  
  
  系统不会严格区分Actino里面的那个属性是用来封装请求参数的属性,那个事用来封装结果的属性,对于系统来说,封装请求参数和封装处理结果的属性石完全平等的.如果用户的HTTP请求里面包含了tip的请求参数,系统就会调用Action的void setTip(String tip)方法,通过这种方式,名为tip的请求参数就可以传给Action实例,如果Action类里面没有包含对应的方法,那么名字为tip的请求参数也就无法传入该Action.
  
  同样,在JSP页面中输出的Action属性的时候,它也不会区分该属性是用来封装请求参数的还是用于封装处理结果的属性,所以说,使用Struts2的标签既可以输出Action的处理结果,也可以输出Http请求参数值.
  
  从上面的代码可以看到,需要在JSP页面中输出的处理结果是一个非常简单的字符串,可以使用标签来控制输出.实际上,Action类里面可以封装非常复杂的属性,包括其他用户自己定义的属性,数组,集合对象和Map对象等等.对于这些复杂类型的输出,一样可以通过Struts2的标签来完成.
  
  为了让用户开发的Action来更加的规范,Struts2提供了一个Action接口,这个接口定义了Struts2的Action处理类应该实现的规范,下面是Action接口的代码:
  
  public interface Action {
  
   public static final String SUCCESS="success";
  
   public static final String ERROR="error";
  
   public static final String INPUT="input";
  
   public static final String LOGIN="login";
  
   public static final String NONE="none";
  
   public String execute()throws Exception;
  
  }
  
  上面的Action接口里面只定义了一个execute方法,该接口的规范规定了Action类应该包含一个execute方法,该方法的作用是返回一个字符串,除此之外,该接口还定义了5个字符串常量,他们的作用是统一execute方法的返回值.
  
  比如说,当Action处理完用户的请求成功之后,有人喜欢返回welcome这种字符串,有的人喜欢返回success字符串…这样不利于项目的统一管理.Struts2的Action定义上面的5个字符串:error,none,input,longin和success等等分别代表了特定的含义,当然,如果开发者依然希望使用特定的字符串做为逻辑视图的名字,开发者依然可以返回自己的视图.
  
  另外,Struts2还提供了Action类的一个实现类:ActionSupport,下面是Action类的代码:
  
  package com.opensymphony.xwork2;
  
  import com.opensymphony.xwork2.util.ValueStack;
  
  import com.opensymphony.xwork2.util.logging.Logger;
  
  import com.opensymphony.xwork2.util.logging.LoggerFactory;
  
   
  
  import java.io.Serializable;
  
  import java.util.*;
  
   
  
  /**
  
   * Provides a default implementation for the most common actions.
  
  */
  
  //系统提供的ActionSupport类
  
  public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
  
   
  
   protected static Logger LOG = LoggerFactory.getLogger(ActionSupport.class);
  
   
  
  private final transient TextProvider textProvider = new TextProviderFactory().createInstance(getClass(), this);
  
   
  
   private final ValidationAwareSupport validationAware = new ValidationAwareSupport();
  
   
  
   //收集校验错误的方法
  
   public void setActionErrors(Collection errorMessages) {
  
   validationAware.setActionErrors(errorMessages);
  
   }
  
   //返回校验错误的方法
  
   public Collection getActionErrors() {
  
   return validationAware.getActionErrors();
  
   }
  
   
  
   public void setActionMessages(Collection messages) {
  
   validationAware.setActionMessages(messages);
  
   }
  
   
  
   public Collection getActionMessages() {
  
   return validationAware.getActionMessages();
  
   }
  
   
  
   /**
  
   * @deprecated Use {@link #getActionErrors()}.
  
   */
  
   @Deprecated public Collection getErrorMessages() {
  
   return getActionErrors();
  
   }
  
   
  
   /**
  
   * @deprecated Use {@link #getFieldErrors()}.
  
   */
  
   @Deprecated public Map> getErrors() {
  
   return getFieldErrors();
  
   }
  
   //设置表单域校验错误信息
  
   public void setFieldErrors(Map> errorMap) {
  
   validationAware.setFieldErrors(errorMap);
  
   }
  
   //返回表单域校验错误信息
  
   public Map> getFieldErrors() {
  
   return validationAware.getFieldErrors();
  
   }
  
   //控制locale的相关信息
  
   public Locale getLocale() {
  
   ActionContext ctx = ActionContext.getContext();
  
   if (ctx != null) {
  
   return ctx.getLocale();
  
   } else {
  
   LOG.debug("Action context not initialized");
  
   return null;
  
   }
  
   }
  
   
  
   public boolean hasKey(String key) {
  
   return textProvider.hasKey(key);
  
   }
  
   //返回国际化信息的方法
  
   public String getText(String aTextName) {
  
   return textProvider.getText(aTextName);
  
   }
  
   
  
   public String getText(String aTextName, String defaultValue) {
  
   return textProvider.getText(aTextName, defaultValue);
  
   }
  
   
  
   public String getText(String aTextName, String defaultValue, String obj) {
  
   return textProvider.getText(aTextName, defaultValue, obj);
  
   }
  
   
  
   public String getText(String aTextName, List args) {
  
   return textProvider.getText(aTextName, args);
  
   }
  
   
  
   public String getText(String key, String[] args) {
  
   return textProvider.getText(key, args);
  
   }
  
   
  
   public String getText(String aTextName, String defaultValue, List args) {
  
   return textProvider.getText(aTextName, defaultValue, args);
  
   }
  
   
  
   public String getText(String key, String defaultValue, String[] args) {
  
   return textProvider.getText(key, defaultValue, args);
  
   }
  
   
  
   public String getText(String key, String defaultValue, List args, ValueStack stack) {
  
   return textProvider.getText(key, defaultValue, args, stack);
  
   }
  
   
  
   public String getText(String key, String defaultValue, String[] args, ValueStack stack) {
  
   return textProvider.getText(key, defaultValue, args, stack);
  
   }
  
   
  
   public ResourceBundle getTexts() {
  
   return textProvider.getTexts();
  
   }
  
   
  
   public ResourceBundle getTexts(String aBundleName) {
  
   return textProvider.getTexts(aBundleName);
  
   }
  
   
  
   public void addActionError(String anErrorMessage) {
  
   validationAware.addActionError(anErrorMessage);
  
   }
  
   
  
   public void addActionMessage(String aMessage) {
  
   validationAware.addActionMessage(aMessage);
  
   }
  
   
  
   public void addFieldError(String fieldName, String errorMessage) {
  
   validationAware.addFieldError(fieldName, errorMessage);
  
   }
  
   
  
   public String input() throws Exception {
  
   return INPUT;
  
   }
  
   
  
   public String doDefault() throws Exception {
  
   return SUCCESS;
  
   }
  
   
  
   /**
  
   * A default implementation that does nothing an returns "success".
  
   *


  
   * Subclasses should override this method to provide their business logic.
  
   *


  
   * See also {@link com.opensymphony.xwork2.Action#execute()}.
  
   *
  
   * @return returns {@link #SUCCESS}
  
   * @throws Exception can be thrown by subclasses.
  
   */
  
  //默认的处理用户请求的方法,直接返回success字符串
  
   public String execute() throws Exception {
  
   return SUCCESS;
  
   }
  
   
  
   public boolean hasActionErrors() {
  
   return validationAware.hasActionErrors();
  
   }
  
   
  
   public boolean hasActionMessages() {
  
   return validationAware.hasActionMessages();
  
   }
  
   
  
   public boolean hasErrors() {
  
   return validationAware.hasErrors();
  
   }
  
   
  
   public boolean hasFieldErrors() {
  
   return validationAware.hasFieldErrors();
  
   }
  
   
  
   /**
  
   * Clears field errors. Useful for Continuations and other situations
  
   * where you might want to clear parts of the state on the same action.
  
   */
  
   public void clearFieldErrors() {
  
   validationAware.clearFieldErrors();
  
   }
  
   
  
   /**
  
   * Clears action errors. Useful for Continuations and other situations
  
   * where you might want to clear parts of the state on the same action.
  
   */
  
   public void clearActionErrors() {
  
   validationAware.clearActionErrors();
  
   }
  
   
  
   /**
  
   * Clears messages. Useful for Continuations and other situations
  
   * where you might want to clear parts of the state on the same action.
  
   */
  
   public void clearMessages() {
  
   validationAware.clearMessages();
  
   }
  
   
  
   /**
  
   * Clears all errors. Useful for Continuations and other situations
  
   * where you might want to clear parts of the state on the same action.
  
   */
  
   public void clearErrors() {
  
   validationAware.clearErrors();
  
   }
  
   
  
   /**
  
   * Clears all errors and messages. Useful for Continuations and other situations
  
   * where you might want to clear parts of the state on the same action.
  
   */
  
   public void clearErrorsAndMessages() {
  
   validationAware.clearErrorsAndMessages();
  
   }
  
   
  
   /**
  
   * A default implementation that validates nothing.
  
   * Subclasses should override this method to provide validations.
  
   */
  
   //包含空的输入校验方法
  
   public void validate() {
  
   }
  
   
  
   @Override public Object clone() throws CloneNotSupportedException {
  
   return super.clone();
  
  }
  
   
  
   public void pause(String result) {
  
   }
  
   
  
  }
  
  正如上面的代码看到的,该Action是一个默认的Action类,该类里面已经提供了很多默认的方法,这些默认的方法包括获取国际化信息的方法,数据校验的方法,默认的处理用户请求的方法等等,实际上,ActionSupport类是Struts2默认的Action处理类,如果让开发者的Action类继承该ActionSupport来,就会大大的简化Action的开发.
  
  (当用户配置的Action类没有指定class属性的时候,系统会自动使用ActionSupport类作为默认的Action处理类)
  
  2) Action访问Servlet API
  
  Struts2的Action并没有直接和任何的Servlet API进行耦合,这是Struts2的一个改良之处,因为Action类不再和Servlet API耦合,就可以更加轻松的去测试该Action,就不用mock去模拟数据测试了.
  
  但是对于web应用的控制器而言,不访问Servlet API几乎是不可能的,比如说跟踪HTTP Session的状态等等.Struts2框架提供了一种更加轻松的方式来访问Servlet API,Web应用中通常需要访问的Servlet API就是HttpServletRequest,HttpSession和HttpContext,这三个类分别代表JSP内置对象里面的request,session和application.
  
  Struts2提供了一个ActionContext类,Struts2的Action可以通过该类来访问Servlet API,下面是ActionContext类中包含的几个常用的方法:
  
  l Object get(Object key):该方法类似于调用HttpServletRequest的getAttribute(String name)方法.
  
  l Map getApplication():返回一个Map对象,该对象模拟了该应用的ServletContext实例.
  
  l static ActionContext getContext():静态方法,获取系统的ActionContext实例.
  
  l Map getParameters():获取所有的请求参数,类似于调用HttpServletRequest对象的getParameterMap方法.
  
  l Map getSession():返回一个Map对象,这个Map对象模拟了HttpSession实例.
  
  l void setApplication(Map application):直接传入一个Map实例,将该Map实例里面的key-value对转换成application的属性名属性值.
  
  l void setSession(Map session):直接传入一个Map实例,将该Map实例里面的key-value对转换成session的属性名和属性值.
  
  下面的一个应用将在Action类中通过ActionContext访问ServletAPI:
  
  ① 系统提交请求到LoginAction,LoginAction的代码如下:
  
  package com.supermos.app.Action;
  
   
  
  import com.opensymphony.xwork2.Action;
  
  import com.opensymphony.xwork2.ActionContext;
  
   
  
  public class LoginAction implements Action{
  
   private String username;
  
   private String password;
  
   public String getUsername() {
  
   return username;
  
   }
  
   public void setUsername(String username) {
  
   this.username = username;
  
   }
  
   public String getPassword() {
  
   return password;
  
   }
  
   public void setPassword(String password) {
  
   this.password = password;
  
   }
  
   public String execute(){
  
   //获取ActionContext实例,通过该实例访问Servlet API
  
   ActionContext ctx=ActionContext.getContext();
  
   //获取ServletContext里面的counter属性
  
   Integer counter=(Integer)ctx.getApplication().get("counter");
  
   //如果counter的属性为空,就设置该counter的属性为1
  
   if(counter==null){
  
   counter=1;
  
   }
  
   //否则,将counter加1
  
   else{
  
   counter=counter+1;
  
   }
  
   //将增加1之后的counter的值设置为counter属性
  
   ctx.getApplication().put("counter", counter);
  
   //将登陆用的username属性设置为一个HttpSession属性
  
   ctx.getSession().put("user",this.getUsername());
  
   if("supermos".equalsIgnoreCase(this.getUsername())&&"ziwen".equalsIgnoreCase(this.getPassword())){
  
   //直接设置HttpServletRequest属性,下面的代码的作用类似于设置HttpServletRequest属性
  
   //request.setAttribute("tip","服务器提示,您已经成功登陆");
  
   ctx.put("tip", "服务器提示,您已经成功登陆");
  
   return this.SUCCESS;
  
   }else{
  
   //直接设置HttpServletRequest属性
  
   ctx.put("tip","服务器提示,您登陆失败,请检查您的用户名和密码");
  
   return this.ERROR;
  
   }
  
   }
  
  }
  
  ② 上面的Action访问了HttpServletRequest的属性,也访问了HttpSession的属性,还让问了HttpContext的属性,将该Action配置在struts.xml文件中,struts.xml文件的代码如下:
  
  
  
     
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  
   "http://struts.apache.org/dtds/struts-2.0.dtd">
  
   
  
   
  
   
  
   /success.jsp
  
   /error.jsp
  
   /login.jsp
  
   
  
   
  
   
  
  ③ 如果浏览者在页面中输入用户名supermos密码ziwen的时候,将提交给LoginAction来进行处理,该Action的execute方法分别设置了HttpServletRequest属性,HttpSession属性和ServletContext属性,为了验证是否成功设置了这三个属性,我们在success.jsp页面中访问这三个对象的属性:代码如下:
  
  <%@ page language="java" pageEncoding="utf-8"%>
  
  <%@ taglib uri="/struts-tags" prefix="s" %>
  
   
  
  
  
  
  
   
  
   
  
   
  
   
  
   <body>   
  
   本站的访问次数为:${applicationScope.counter}


  
   
  
   ${sessionScope.user },您已经登录!


  
   ${requestScope.tip }
  
   </body>
  
  
  
  显然,相比Struts1中直接访问ServletAPI,Strut2通过ActionContext访问ServletAPI更加的优雅,让Action彻底从ServletAPI中分离出来,从而可以允许该Action脱离web容器.最大的好处就是可以脱离web容器测试Action.
  
  3) Action直接访问ServletAPI
  
  虽然Struts2提供了ActionContext来访问ServletAPI,但是这种方式毕竟不能够直接获得ServletAPI的实例,为了在Action中直接访问ServletAPI,Struts2还提供了如下的几个接口:
  
  l ServletContextAware:实现该接口的Action可以直接访问Web应用的ServletContext实例.(aware[əˈwɛə]意识到的,知道的)
  
  l ServletRequestAware:实现该接口的Action可以直接访问服务器相应的HttpServletRequest实例
  
  l ServletResponseAware:实现该接口的Action可以直接访问服务器相应的HttpServletResponse实例.
  
  下面以ServletResponseAware为例子,介绍如何在Action中访问HttpServletResponse对象,本应用通过HttpServletResponse为系统添加Cookie对象.下面是该上面例子的Action的修改:
  
  package com.supermos.app.Action;
  
   
  
  import javax.servlet.http.Cookie;
  
  import javax.servlet.http.HttpServletResponse;
  
   
  
  import org.apache.struts2.interceptor.ServletResponseAware;
  
   
  
  import com.opensymphony.xwork2.Action;
  
  import com.opensymphony.xwork2.ActionContext;
  
   
  
  public class LoginAction implements Action,ServletResponseAware{
  
   //需要访问的HttpServletResponse对象
  
   private HttpServletResponse response;
  
   private String username;
  
   private String password;
  
   public String getUsername() {
  
   return username;
  
   }
  
   public void setUsername(String username) {
  
   this.username = username;
  
   }
  
   public String getPassword() {
  
   return password;
  
   }
  
   public void setPassword(String password) {
  
   this.password = password;
  
   }
  
   //实现ServletResponseAware接口所必须要实现的一个方法
  
   public void setServletResponse(HttpServletResponse response) {
  
   //在该方法内就可以访问web应用对客户的响应对象
  
   this.response = response;
  
   }
  
   public String execute(){
  
   //获取ActionContext实例,通过该实例访问Servlet API
  
   ActionContext ctx=ActionContext.getContext();
  
   //获取ServletContext里面的counter属性
  
   Integer counter=(Integer)ctx.getApplication().get("counter");
  
   //如果counter的属性为空,就设置该counter的属性为1
  
   if(counter==null){
  
   counter=1;
  
   }
  
   //否则,将counter加1
  
   else{
  
   counter=counter+1;
  
   }
  
   //将增加1之后的counter的值设置为counter属性
  
   ctx.getApplication().put("counter", counter);
  
   //将登陆用的username属性设置为一个HttpSession属性
  
   ctx.getSession().put("user",this.getUsername());
  
   if("supermos".equalsIgnoreCase(this.getUsername())&&"ziwen".equalsIgnoreCase(this.getPassword())){
  
   //直接设置HttpServletRequest属性,下面的代码的作用类似于设置HttpServletRequest属性
  
   //request.setAttribute("tip","服务器提示,您已经成功登陆");
  
   ctx.put("tip", "服务器提示,您已经成功登陆");
  
   
  
   //创建一个user对象
  
   Cookie c=new Cookie("user",this.getUsername());
  
   //设置cookie对象的最大的生存时间
  
   c.setMaxAge(60*60);
  
   //使用HttpServletResponse来添加Cookie对象
  
   response.addCookie(c);
  
   return this.SUCCESS;
  
   }else{
  
   //直接设置HttpServletRequest属性
  
   ctx.put("tip","服务器提示,您登陆失败,请检查您的用户名和密码");
  
   return this.ERROR;
  
   }
  
   }
  
  }
  
  通过查看Struts2的API的相关文档,发现,实现ServletResponseAware,仅仅需要实现如下的方法:
  
  public void setServletResponse(HttpServletResponse response)
  
  实现了上面的方法的时候,该方法内有一个HttpServletResponse参数,该参数就代表了web应用中对客户端的一个相应对象,并将该对象设置成Action的成员属性,从而允许在execute方法中访问该HttpServletResponse对象.
  
  于此类似的是,如果一个Action实现了ServletRequestAware接口,就必须要实现如下的方法:
  
  public void setServletRequest(HttpServletRequest request)
  
  通过实现上面的方法,从而允许Action直接访问HttpServletRequest对象.
  
  为了直接访问ServletAPI,Struts2提供了一个ServletActionContext,这个类包含了以下几个静态的方法:
  
  l static PageContext getPageContext():取得Web应用的PageContext对象.
  
  l static HttpServletRequest gettRequest():取得Web应用的HttpServletRequest对象
  
  l static HttpServletResponse getResponse():取得Web应用的HttpServletResponse对象.
  
  l static ServletContext getServletContext():取得Web应用的ServletContext对象
  
  借助于ServletActionContext这个类的帮助,开发者也可以直接在Action中直接访问ServletAPI,却可以避免Action类需要实现XXXAware接口-虽然如此,但该Action却依然需要与ServletAPI直接耦合,一样不利于程序的解耦.
  
  (可以通过ServletActionContext类的帮助,从而以更加简单的方式来直接的访问ServletAPI)
  
  当该Action处理完用户的请求之后,web应用就向客户机中添加了一个Cookie对象下面的JSP页面通过表达式来访问Cookie的值,该JSP页面的代码如下:
  
  <%@ page language="java" pageEncoding="utf-8"%>
  
  <%@ taglib uri="/struts-tags" prefix="s" %>
  
   
  
  
  
  
  
   
  
   
  
   
  
   
  
   <body>   
  
   本站的访问次数为:${applicationScope.counter}


  
   
  
   ${sessionScope.user },您已经登录!


  
   ${requestScope.tip }
  
  
============传说中的分割线,以下为了验证Cookie===============




  
   
  
   从系统中读取的Cookie值:${cookie.user.value}
  
   </body>
  
  
  
  必须应该指出的问题是,虽然可以在Action中获取HttpServletResponse,但是如果希望HttpServletResponse来生成服务器相应是不可能的,因为Action只是控制器,也就是说,如果在Action中书写如下的代码response.getWriter().println(“Helloworld”);上面的代码在标准的Servlet中会生成对客户端的输出,但是在Struts2的Action中,就没有任何的实际意义了.
  
  (提示:即使我们在Struts2的Action类中获得了HttpServletResponse的对象,也不要尝试直接在Action中生成对客户端的输出.!!!!)
  
  4) 配置Action
  
  一旦提供了Action的实现类之后,我们就可以在struts.xml当中对该Action进行配置,配置Action就是让Struts2容器知道该Action的存在,并且可以调用该Action来处理用户的请求,因此,我们认为,Action是Struts2的基本”程序单位”.
  
  Struts2使用包来组织Action,因此将Action定义是放在包定义下面来完成的,定义Action通过使用package下的action子元素来完成,定义Action的时候,至少需要制定该Action的name属性,该name属性既是该Action的名字,也是该Action需要处理的URL的前半部分.
  
  (Struts2的Action名字就是它所要处理的URL的前半部分,与Struts1不同,Struts1中Action配置中的name属性指定的是该Action关联的ActionForm,而path属性才是该Action要处理的URL,可以理解,Struts2中的Action的name属性就等同于Struts1中Action的path属性)
  
  除此之外,通常还要为action指定一个class属性,其中class属性指定了改Action的实现类.
  
  (该class属性并不是必需的,如果我们不为元素指定class属性,系统就会默认使用系统的ActionSupport类来进行处理)
  
  因此,一个Action的配置片段如下:
  
  
  
   
  
   
  
  
  
  Action只是一个控制器,它并不直接对浏览者产生任何的响应,因此Action处理完用户的请求之后,Action需要将制定的视图资源呈现给用户.因此配置Action的时候,应该配置逻辑视图和物理视图资源之间的映射.
  
  配置逻辑视图和物理视图之间的映射关系是通过元素来定义的,每个元素定义逻辑视图和物理视图之间的一次映射关系.
  
  完整的Action配置定义如下:
  
  
  
     
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  
   "http://struts.apache.org/dtds/struts-2.0.dtd">
  
   
  
   
  
   
  
   /success.jsp
  
   /error.jsp
  
   /login.jsp
  
   
  
   
  
   
  
  定义元素的时候,需要指定name属性,通常name属性都是有字母和数字组成的,如果要在name属性中使用斜线/,则需要指定Struts2框架允许ActionName中出现斜线,设置允许Action name中出现斜线是通过struts.enable.SlashesInActionNames常量来制定,设置该常量的值为true,既允许Struts2框架中使用斜线.
  
  (关于Action的name属性的命名规则,Action的name命名是非常灵活的,但如果name属性分配一个带点.或者带中划线-的值,比如说my.user或者是my-action等等,则可能会引发一些未知的一场.因此不推荐在Action的name中使用点和中划线)
  5) 动态方法的调用
  
  Struts1框架提供了一个DispatchAction,从而允许一个Action里面包含多个处理逻辑,比如说对于同一个表单,当用户通过不同的请求按钮提交表单的时候,应该使用Action的不同的方法来处理请求.
  
  Struts2同样提供了这种处理多个请求的Action.可以采用DMI(Dynamic Method Invocation动态方法)调用来处理一个表单多种请求的情况,动态方法调用是指表单元素的action并不是直接等于某个Action的名字,而是以如下形式来制定Form的action属性.
  
  
  
  action=”ActionName!methodName.action”
  
  
  
  
  
  单机”注册”按钮的时候将会激发regist函数,该函数的代码如下:
  
  function regist(){
  
   //获取JSP页面中一个表单元素
  
   targetForm=document.forms[0];
  
   //东该修改目标表单的action属性
  
   targetForm.action=”Login!regist.action”;
  
   //提交表单
  
  targetForm.submit();
  
  }
  
  上面JavaScript代码的核心在于动态的修改表单元素的Action属性,修改之后action属性为Login!regist.action,其实质就是将该表单提交给loginAction的regist方法进行处理.
  
  LoginAction的代码如下:
  
  package com.supermos.app.Action;
  
   
  
  import javax.servlet.http.Cookie;
  
  import javax.servlet.http.HttpServletResponse;
  
   
  
  import org.apache.struts2.interceptor.ServletResponseAware;
  
   
  
  import com.opensymphony.xwork2.Action;
  
  import com.opensymphony.xwork2.ActionContext;
  
   
  
  public class LoginAction implements Action,ServletResponseAware{
  
   //需要访问的HttpServletResponse对象
  
   private HttpServletResponse response;
  
   private String username;
  
   private String password;
  
   private String tip;
  
   public HttpServletResponse getResponse() {
  
   return response;
  
   }
  
   public void setResponse(HttpServletResponse response) {
  
   this.response = response;
  
   }
  
   public String getTip() {
  
   return tip;
  
   }
  
   public void setTip(String tip) {
  
   this.tip = tip;
  
   }
  
   public String getUsername() {
  
   return username;
  
   }
  
   public void setUsername(String username) {
  
   this.username = username;
  
   }
  
   public String getPassword() {
  
   return password;
  
   }
  
   public void setPassword(String password) {
  
   this.password = password;
  
   }
  
   //实现ServletResponseAware接口所必须要实现的一个方法
  
   public void setServletResponse(HttpServletResponse response) {
  
   //在该方法内就可以访问web应用对客户的响应对象
  
   this.response = response;
  
   }
  
   public String regist(){
  
   ActionContext.getContext().getSession().put("user", this.getUsername());
  
   this.setTip("恭喜您"+this.getUsername()+",您注册成功了");
  
   return this.SUCCESS;
  
   }
  
   public String execute(){
  
   //获取ActionContext实例,通过该实例访问Servlet API
  
   ActionContext ctx=ActionContext.getContext();
  
   //获取ServletContext里面的counter属性
  
   Integer counter=(Integer)ctx.getApplication().get("counter");
  
   //如果counter的属性为空,就设置该counter的属性为1
  
   if(counter==null){
  
   counter=1;
  
   }
  
   //否则,将counter加1
  
   else{
  
   counter=counter+1;
  
   }
  
   //将增加1之后的counter的值设置为counter属性
  
   ctx.getApplication().put("counter", counter);
  
   //将登陆用的username属性设置为一个HttpSession属性
  
   ctx.getSession().put("user",this.getUsername());
  
   if("supermos".equalsIgnoreCase(this.getUsername())&&"ziwen".equalsIgnoreCase(this.getPassword())){
  
   //直接设置HttpServletRequest属性,下面的代码的作用类似于设置HttpServletRequest属性
  
   //request.setAttribute("tip","服务器提示,您已经成功登陆");
  
   ctx.put("tip", "服务器提示,您已经成功登陆");
  
   
  
   //创建一个user对象
  
   Cookie c=new Cookie("user",this.getUsername());
  
   //设置cookie对象的最大的生存时间
  
   c.setMaxAge(60*60);
  
   //使用HttpServletResponse来添加Cookie对象
  
   response.addCookie(c);
  
   return this.SUCCESS;
  
   }else{
  
   //直接设置HttpServletRequest属性
  
   ctx.put("tip","服务器提示,您登陆失败,请检查您的用户名和密码");
  
   return this.ERROR;
  
   }
  
   }
  
  }
  
  当浏览者点击”注册”按钮的时候,系统将提交给login Action的regist方法处理.通过这种方式,我们可以再一个Action中包含多个处理逻辑,并可以通过为表单元素指定不同的action属性来提交给Action的不同的方法.
  
  对于使用动态方法调用的方法,比如说regist方法,该方法的方法声明与系统默认的execute方法的方法声明只有方法名字不相同,其他的比如方法的参数,返回值的类型都应该绝对的相同.
  
  (使用动态方法调用前必须设置Struts2允许动态方法调用,开启系统的动态方法调用是通过设置struts.enable.DynamicMethodInvocation常量来完成的,设置该常量的值为true将开启动态方法调用,否者将关闭动态方法调用)
  
  6) 为action元素指定method属性
  
  对于一个表单中有含有两个不同的按钮,分别提交给不同的处理逻辑的情况下,Struts2还提供了一种另外的方法,即将一个Action处理类定义成多个逻辑Action.如果在配置元素的时候,指定action的method属性,则可以让Action类调用指定方法,而不是execute方法来处理用户的请求.
  
  比如说,我们可以有如下的配置片段:
  
  
  
     
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  
   "http://struts.apache.org/dtds/struts-2.0.dtd">
  
   
  
   
  
   
  
   /success.jsp
  
   /error.jsp
  
   /login.jsp
  
   
  
   
  
   /success.jsp
  
   /error.jsp
  
   /login.jsp
  
   
  
   
  
   
  
  上面定义了Login和Regist两个逻辑Action,它们对应的处理类都是com.supermos.app.Action.LoginAction,Login和Regist两个Action虽然有相同的处理类,但是处理逻辑不一样----处理逻辑通过method方法来制定,其中名为login的Action对应的处理逻辑为默认的execute方法,而regist的Action对应的逻辑处理为指定的regist方法.
  
  将一个Action处理类定义成两个Action之后,可以再修改JSP页面的JavaScript代码,修改regist函数的代码为如下的形式:
  
   <script type="text/javascript">
  
   function regist(){
  
   var form=document.forms[0];
  
   form.action=" regist.action";
  
   form.submit();
  
   }
  
   </script>
  
  通过这种方式,一样可以完成上面的效果,当浏览者点击”登录”按钮的时候,将提交给Action的登录逻辑处理,当浏览者单击”注册”按钮的时候,将提交给Action类的注册逻辑处理.
  
  (这种将一个Action类映射成多个逻辑Action的方式,非常类似于Struts1的MappingDispachAction的处理方式,在Struts1中,将一个MappingDispachAction类可以定义成多个逻辑Action)
  
  我们再次看到上面struts.xml文件中两个元素定义的时候,我们发现两个action定义的绝大部分是相同的,因此,这种方式相当的冗余,为了解决这个问题,Struts2还有另外一种形式的动态方法的调用,也就是使用通配符的形式.
  
  7) 使用通配符
  
  在配置元素的时候,需要指定name,class还有method属性,这三个属性都可以支持通配符,这种使用通配符的方式是另外的一种动态的方法的调用,当我们使用通配符定义Action的name属性的时候,就相当于一个元素Action定义多个逻辑Action.
  
  看下面的struts.xml配置文件的代码:
  
  
  
     
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  
   "http://struts.apache.org/dtds/struts-2.0.dtd">
  
   
  
   
  
   
  
   
  
   
  
   /success.jsp
  
   /error.jsp
  
   /login.jsp
  
   
  
   
  
   
  
  (代码中是name=”*Action”不要写成了”*Action.action”…..2009年09月02日)
  
  上面的不是定义了一个普通的Action,而是定义了一系列的逻辑Action—只要用户请求的URL是*Action.action的模式,都可以通过该Action类来进行处理.配置该action元素的时候,还制定method属性(method属性用于指定处理用户请求的方法),但该method属性使用了一个表达式{0},该表达式的值就是name属性值中第一个*的值.例如,如果用户请求的URL为loginAction.action,则调用com.supermos.app.Action.LoginAction的login方法,如果请求URL为registAction.action,这调用的是com.supermos.app.Action.LoginAction的regist方法.
  
  下面是本应用的LoginAction类的代码:
  
  package com.supermos.app.Action;
  
   
  
  import javax.servlet.http.Cookie;
  
  import javax.servlet.http.HttpServletResponse;
  
   
  
  import org.apache.struts2.interceptor.ServletResponseAware;
  
   
  
  import com.opensymphony.xwork2.Action;
  
  import com.opensymphony.xwork2.ActionContext;
  
   
  
  public class LoginAction implements Action,ServletResponseAware{
  
   //需要访问的HttpServletResponse对象
  
   private HttpServletResponse response;
  
   private String username;
  
   private String password;
  
   private String tip;
  
   public HttpServletResponse getResponse() {
  
   return response;
  
   }
  
   public void setResponse(HttpServletResponse response) {
  
   this.response = response;
  
   }
  
   public String getTip() {
  
   return tip;
  
   }
  
   public void setTip(String tip) {
  
   this.tip = tip;
  
   }
  
   public String getUsername() {
  
   return username;
  
   }
  
   public void setUsername(String username) {
  
   this.username = username;
  
   }
  
   public String getPassword() {
  
   return password;
  
   }
  
   public void setPassword(String password) {
  
   this.password = password;
  
   }
  
   //实现ServletResponseAware接口所必须要实现的一个方法
  
   public void setServletResponse(HttpServletResponse response) {
  
   //在该方法内就可以访问web应用对客户的响应对象
  
   this.response = response;
  
   }
  
   public String regist(){
  
   ActionContext.getContext().getSession().put("user", this.getUsername());
  
   this.setTip("恭喜您"+this.getUsername()+",您注册成功了");
  
   return this.SUCCESS;
  
   }
  
   public String login(){
  
   //获取ActionContext实例,通过该实例访问Servlet API
  
   ActionContext ctx=ActionContext.getContext();
  
   //获取ServletContext里面的counter属性
  
   Integer counter=(Integer)ctx.getApplication().get("counter");
  
   //如果counter的属性为空,就设置该counter的属性为1
  
   if(counter==null){
  
   counter=1;
  
   }
  
   //否则,将counter加1
  
   else{
  
   counter=counter+1;
  
   }
  
   //将增加1之后的counter的值设置为counter属性
  
   ctx.getApplication().put("counter", counter);
  
   //将登陆用的username属性设置为一个HttpSession属性
  
   ctx.getSession().put("user",this.getUsername());
  
   if("supermos".equalsIgnoreCase(this.getUsername())&&"ziwen".equalsIgnoreCase(this.getPassword())){
  
   //直接设置HttpServletRequest属性,下面的代码的作用类似于设置HttpServletRequest属性
  
   //request.setAttribute("tip","服务器提示,您已经成功登陆");
  
   ctx.put("tip", "服务器提示,您已经成功登陆");
  
   
  
   //创建一个user对象
  
   Cookie c=new Cookie("user",this.getUsername());
  
   //设置cookie对象的最大的生存时间
  
   c.setMaxAge(60*60);
  
   //使用HttpServletResponse来添加Cookie对象
  
   response.addCookie(c);
  
   return this.SUCCESS;
  
   }else{
  
   //直接设置HttpServletRequest属性
  
   ctx.put("tip","服务器提示,您登陆失败,请检查您的用户名和密码");
  
   return this.ERROR;
  
   }
  
   }
  
  }
  
  上面的Action类不再包含默认的execute方法,而是包含了regist和login两个方法,这两个方法与execute方法除了方法名字不相同之外,其他的完全相同.
  
  我们修改JavaScript中regist函数的代码为如下的形式:
  
   <script type="text/javascript">
  
   function regist(){
  
   //获取页面中的第一个表单元素
  
   var form=document.forms[0];
  
   //动态修改表单的action属性
  
   form.action="registAction.action";
  
   //提交表单
  
   form.submit();
  
   }
  
   </script>
  
  在上面的方法中看到,当浏览者单击”注册”按钮的时候,动态修改表单的action属性为registAction.action,该请求匹配了*Action的形式,将交给Action处理:registAction匹配*Action模式的时候.*的值为regist,则调用regist方法来处理用户的请求.
  
  除此之外,表达式也可以出现在元素的class属性当中,也就是Struts2允许将一系列的Action配置成一个元素.
  
  看下面的struts.xml配置文件的片段:
  
  
  
     
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  
   "http://struts.apache.org/dtds/struts-2.0.dtd">
  
   
  
   
  
   
  
   /login.jsp
  
   /error.jsp
  
   /welcome.jsp
  
   
  
   
  
   
  
  上面的片段定义了一系列的Action,这系列的Action名字应该匹配*Action的模式,没有指定method属性,也就是说,总是使用execute方法来处理用户的请求,但是class属性值使用了表达式,上面的配置片段的含义是,如果有URL为RegistAction.action请求,就可以匹配*Action模式,将交给该Action处理,其第一个*的值为Regist,将该Regist传入class属性值,也就是该Action的处理类为com.supermos.app.Action.RegistAction.
  
  如果有需要,Struts2允许在class属性和method属性中同时使用表达式,看一下下面饿一个配置片段.
  
  
  
  
  
  上面的定义片段定义了一个模式为*_*的Action,也就是说,只要匹配该模式的请求,都可以被该Action处理.如果有URL为Book_save.action的请求,因为匹配了*_*的模式,并且第一个*的值为Book,第二个*的值为save,就意味着可以调用Book处理类的save方法来处理用户的请求.
  
  实际上Struts2不仅允许在class属性,name属性中使用表达式,还可以在元素的result子元素中使用表达式,下面提供了一个通用Action,该Action可以配置成如下的形式:
  
   
  
   
  
   
  
   /{1}.jsp
  
   
  
  在上面的Action的定义中,Action的名字是一个*,它可以匹配任意的Action.所有的用户请求都可以通过该Action来进行处理,因为没有为该Action指定class属性,也就是说该Action使用ActionSupport来作为处理类,而且因为该ActionSupport类的execute方法返回success字符串,即该Action总是直接返回result中指定的JSP资源,JSP资源使用了表达式来生成资源名,上面的Action定义的含义是:
  
  如果请求a.action,则进入a.jsp,如果请求b.action则进入b.jsp页面,以此类推.
  
  现在的问题是,当用户请求的URL同时匹配多个Action的时候,究竟由哪个Action来进行处理?
  
  不如说,现在有URL为abcAction.action的请求,在Struts.xml文件中配置了如下的三个Action,他们的Action name的值分别为abcAction,*Action还有*,则这个请求名将被abcAction的Action进行处理.
  
  如果有URL为defAction.action的请求,struts.xml文件中同样配置了abcAction,*Action和*的一个Action,defAction.action的请求显然不会被name为abcAction的Action处理,到底是被name为*Action处理还是被*的Action来处理呢?
  
  如果有URL为abcAction.action的请求,如果struts.xml文件中没有名为abcAction的Action,则一定由该Action来处理用户请求:如果struts.xml文件中没有名为abcAction的Action,则搜寻name属性值匹配abcAction的Action,例如name为*Action或者*,*Action并不会比*更加优先匹配abcAction的请求,而是先找到那个Action,就先有那个Action来处理用户的请求.
  
  (因为除非请求的URL与Action name属性绝对相同,否则将按照先后顺序来决定哪个Action来处理用户的请求,因此,我们在写配置文件的时候,要尽量将*的Action配置到最后,否者Struts2将使用该Action来处理所有希望使用模式匹配的请求)
  
  8) 默认的Action
  
  在某些情况下,用户请求非常简单,不需要系统过多的处理,或者这些请求只是一个简单的转发作用.
  
  (提示: 对于使用Struts2框架的应用而言,尽量不要让超级链接直接连接到某个视图资源,因为这种方式增加了额外的风险,推荐将所有的请求都发送给Struts2框架,让该框架来处理用户的请求,哪怕是只是简单的超级链接).
  
  对于只是简单的超级链接的请求,可以通过定义name为*的Action(该Action应该放在最后定义)实现,除此之外,Struts2还允许在容器中定义一个默认的Action,当用户的请求的URL在容器中找不到对应的Action的时候,系统将使用默认的Action来处理用户的请求.
  
  配置默认的Action通过元素完成,每个元素配置一个默认Action下面的struts.xml配置片段配置了一个默认的Action.
  
  
  
     
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  
   "http://struts.apache.org/dtds/struts-2.0.dtd">
  
   
  
   
  
   
  
   
  
   /success.jsp
  
   
  
   
  
   
  
   
  
  从上面的配置文件就可以看到,配置默认的Action只需要配置元素就可以了,配置该元素的时候需要指定一个name属性,该name属性指向容器中另外一个有效的Action,该Action将成为该容器中默认的Action.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值