Struts2的Action(下)

5) 动态方法的调用

Struts1框架提供了一个DispatchAction,从而允许一个Action里面包含多个处理逻辑,比如说对于同一个表单,当用户通过不同的请求按钮提交表单的时候,应该使用Action的不同的方法来处理请求.

Struts2同样提供了这种处理多个请求的Action.可以采用DMI(Dynamic Method Invocation动态方法)调用来处理一个表单多种请求的情况,动态方法调用是指表单元素的action并不是直接等于某个Action的名字,而是以如下形式来制定Formaction属性.

<!- -action的属性为actionName!methodName的形式 -->

action=”ActionName!methodName.action”

<!- - 注册按钮是一个没有任何动作的按钮,但单机该按钮的时候会触发regist函数-- >

<input type=”button” value=”注册” onClick=”regist()”/>

单机注册按钮的时候将会激发regist函数,该函数的代码如下:

function regist(){

//获取JSP页面中一个表单元素

targetForm=document.forms[0];

//东该修改目标表单的action属性

targetForm.action=”Login!regist.action”;

//提交表单

targetForm.submit();

}

上面JavaScript代码的核心在于动态的修改表单元素的Action属性,修改之后action属性为Login!regist.action,其实质就是将该表单提交给loginActionregist方法进行处理.

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;

}

//否则,counter1

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 Actionregist方法处理.通过这种方式,我们可以再一个Action中包含多个处理逻辑,并可以通过为表单元素指定不同的action属性来提交给Action的不同的方法.

对于使用动态方法调用的方法,比如说regist方法,该方法的方法声明与系统默认的execute方法的方法声明只有方法名字不相同,其他的比如方法的参数,返回值的类型都应该绝对的相同.

(使用动态方法调用前必须设置Struts2允许动态方法调用,开启系统的动态方法调用是通过设置struts.enable.DynamicMethodInvocation常量来完成的,设置该常量的值为true将开启动态方法调用,否者将关闭动态方法调用)

6) action元素指定method属性

对于一个表单中有含有两个不同的按钮,分别提交给不同的处理逻辑的情况下,Struts2还提供了一种另外的方法,即将一个Action处理类定义成多个逻辑Action.如果在配置<action…/>元素的时候,指定actionmethod属性,则可以让Action类调用指定方法,而不是execute方法来处理用户的请求.

比如说,我们可以有如下的配置片段:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<package name="supermos" extends="struts-default">

<action name="login" class="com.supermos.app.Action.LoginAction">

<result name="success">/success.jsp</result>

<result name="error">/error.jsp</result>

<result name="input">/login.jsp</result>

</action>

<action name="regist" class="com.supermos.app.Action.LoginAction" method="regist">

<result name="success">/success.jsp</result>

<result name="error">/error.jsp</result>

<result name="input">/login.jsp</result>

</action>

</package>

</struts>

上面定义了LoginRegist两个逻辑Action,它们对应的处理类都是com.supermos.app.Action.LoginAction,LoginRegist两个Action虽然有相同的处理类,但是处理逻辑不一样----处理逻辑通过method方法来制定,其中名为loginAction对应的处理逻辑为默认的execute方法,registAction对应的逻辑处理为指定的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的方式,非常类似于Struts1MappingDispachAction的处理方式,Struts1,将一个MappingDispachAction类可以定义成多个逻辑Action)

我们再次看到上面struts.xml文件中两个<action../>元素定义的时候,我们发现两个action定义的绝大部分是相同的,因此,这种方式相当的冗余,为了解决这个问题,Struts2还有另外一种形式的动态方法的调用,也就是使用通配符的形式.

7) 使用通配符

在配置<action../>元素的时候,需要指定name,class还有method属性,这三个属性都可以支持通配符,这种使用通配符的方式是另外的一种动态的方法的调用,当我们使用通配符定义Actionname属性的时候,就相当于一个元素Action定义多个逻辑Action.

看下面的struts.xml配置文件的代码:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<package name="supermos" extends="struts-default">

<!-- 使用通配符配置了Action,method属性是个动态值 -->

<action name="*Action" class="com.supermos.app.Action.LoginAction" method="{0}">

<!-- 定义了三个动态的result -->

<result name="success">/success.jsp</result>

<result name="error">/error.jsp</result>

<result name="input">/login.jsp</result>

</action>

</package>

</struts>

(代码中是name=”*Action”不要写成了”*Action.action”…..20090902)

上面的<action name=”*.Action” …/>不是定义了一个普通的Action,而是定义了一系列的逻辑Action—只要用户请求的URL*Action.action的模式,都可以通过该Action类来进行处理.配置该action元素的时候,还制定method属性(method属性用于指定处理用户请求的方法),但该method属性使用了一个表达式{0},该表达式的值就是name属性值中第一个*的值.例如,如果用户请求的URLloginAction.action,则调用com.supermos.app.Action.LoginActionlogin方法,如果请求URLregistAction.action,这调用的是com.supermos.app.Action.LoginActionregist方法.

下面是本应用的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;

}

//否则,counter1

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方法,而是包含了registlogin两个方法,这两个方法与execute方法除了方法名字不相同之外,其他的完全相同.

我们修改JavaScriptregist函数的代码为如下的形式:

<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方法来处理用户的请求.

除此之外,表达式也可以出现在<action../>元素的class属性当中,也就是Struts2允许将一系列的Action配置成一个<action../>元素.

看下面的struts.xml配置文件的片段:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<package name="supermos" extends="struts-default">

<action name="*Action" class="com.supermos.app.Action.{1}Action">

<result name="input">/login.jsp</result>

<result name="error">/error.jsp</result>

<result name="success">/welcome.jsp</result>

</action>

</package>

</struts>

上面的<action…/>片段定义了一系列的Action,这系列的Action名字应该匹配*Action的模式,没有指定method属性,也就是说,总是使用execute方法来处理用户的请求,但是class属性值使用了表达式,上面的配置片段的含义是,如果有URLRegistAction.action请求,就可以匹配*Action模式,将交给该Action处理,其第一个*的值为Regist,将该Regist传入class属性值,也就是该Action的处理类为com.supermos.app.Action.RegistAction.

如果有需要,Struts2允许在class属性和method属性中同时使用表达式,看一下下面饿一个配置片段.

<!-- 定义了一个action,同时在class属性和method属性中使用表达式 -->

<action name=”*_*” method=”{2}” class=”action.{1}”>

上面的定义片段定义了一个模式为*_*Action,也就是说,只要匹配该模式的请求,都可以被该Action处理.如果有URLBook_save.action的请求,因为匹配了*_*的模式,并且第一个*的值为Book,第二个*的值为save,就意味着可以调用Book处理类的save方法来处理用户的请求.

实际上Struts2不仅允许在class属性,name属性中使用表达式,还可以在<action../>元素的result子元素中使用表达式,下面提供了一个通用Action,Action可以配置成如下的形式:

<!-- 定义一个通用的Action -->

<action name="*">

<!-- 使用表达式定义Result -->

<result>/{1}.jsp</result>

</action>

在上面的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来进行处理?

不如说,现在有URLabcAction.action的请求,Struts.xml文件中配置了如下的三个Action,他们的Action name的值分别为abcAction,*Action还有*,则这个请求名将被abcActionAction进行处理.

如果有URLdefAction.action的请求,struts.xml文件中同样配置了abcAction,*Action*的一个Action,defAction.action的请求显然不会被nameabcActionAction处理,到底是被name*Action处理还是被*Action来处理呢?

如果有URLabcAction.action的请求,如果struts.xml文件中没有名为abcActionAction,则一定由该Action来处理用户请求:如果struts.xml文件中没有名为abcActionAction,则搜寻name属性值匹配abcActionAction,例如name*Action或者*,*Action并不会比*更加优先匹配abcAction的请求,而是先找到那个Action,就先有那个Action来处理用户的请求.

(因为除非请求的URLAction name属性绝对相同,否则将按照先后顺序来决定哪个Action来处理用户的请求,因此,我们在写配置文件的时候,要尽量将*Action配置到最后,否者Struts2将使用该Action来处理所有希望使用模式匹配的请求)

8) 默认的Action

在某些情况下,用户请求非常简单,不需要系统过多的处理,或者这些请求只是一个简单的转发作用.

(提示: 对于使用Struts2框架的应用而言,尽量不要让超级链接直接连接到某个视图资源,因为这种方式增加了额外的风险,推荐将所有的请求都发送给Struts2框架,让该框架来处理用户的请求,哪怕是只是简单的超级链接).

对于只是简单的超级链接的请求,可以通过定义name*Action(Action应该放在最后定义)实现,除此之外,Struts2还允许在容器中定义一个默认的Action,当用户的请求的URL在容器中找不到对应的Action的时候,系统将使用默认的Action来处理用户的请求.

配置默认的Action通过<def-action-ref../>元素完成,每个<default-action-ref../>元素配置一个默认Action下面的struts.xml配置片段配置了一个默认的Action.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<package name="supermos" extends="struts-default">

<default-action-ref name="simpleViewResultAction"/>

<action name="simpleViewResultAction" class="com.supermos.app.Action.LoginAction" method="{1}">

<result name="success">/success.jsp</result>

<!-- 此处省略n -->

</action>

</package>

</struts>

从上面的配置文件就可以看到,配置默认的Action只需要配置<default-action-ref../>元素就可以了,配置该元素的时候需要指定一个name属性,name属性指向容器中另外一个有效的Action,Action将成为该容器中默认的Action.

你可能感兴趣的:(struts2)