【JavaWeb-18】ActionContext存取数据、ValueStack存取值、EL新查找顺序、iterator、OGNL投影、其他标签、UI主题、防重复提交

1、我们之前说过,OGNL上下文包含ActionContext和ValueStack。我们先来说说ActionContext,它是一个大Map,里面装有4个小Map,分别是application、session、request和attr。我们做个测试时往里面存数据然后再取数据。

——我们部署好struts2之后,在index.jsp中使用struts标签库。在正文中写<s:debug></s:debug>,它相当于一个a标签,点击前往可以查看整个OGNL上下文里面的数据。

——我们在动作类的方法中存入数据,存数据有多重方法,总之是获得对应的Map,然后往里面存数据。

public String data(){
        //往大Map里面,也就是ActionContext里存数据
        ActionContext context=ActionContext.getContext();
        context.put("actionContextKey", "actionContextValue");

        //第1种方式:往小Map里面存数据,比如ActionContext里的小Mapsession
        Map<String,Object> s1=context.getSession();
        s1.put("sessionKey1", "sessionValue1");
        //第2种方式:往小Map里面存数据,比如ActionContext里的小Mapsession
        HttpSession s2=ServletActionContext.getRequest().getSession();
        s2.setAttribute("sessionKey2", "sessionValue2");

        //第1种方式,往application里存
        Map<String,Object> a1=context.getApplication();
        a1.put("applicationKey1", "applicationValue1");
        //第2种方式,往application里存
        ServletContext a2=ServletActionContext.getServletContext();
        a2.setAttribute("applicationKey2", "applicationValue2");

        return SUCCESS;
    }

——然后在index.jsp页面里面获取数据:

<!-- 取ActionContext大Map里面的值,用#+key -->
<s:property value="#actionContextKey"/><br>
<!-- 取小Map里的值,需要先取到对应的session和application等。而这些都是大Map的key,所以操作如下 -->
<s:property value="#session.sessionKey1"/><br>
<s:property value="#session.sessionKey2"/><br>
<s:property value="#application.applicationKey1"/><br>
<s:property value="#application.applicationKey2"/><br>

源代码:JavaEE ActionContext存取数据示例

2、ValueStack存取数据。

——和上述案例类似,我们在动作类的方法中写如下代码,用于获取ValueStack的引用,有3种方式,很明显用第三种ActionContext直接获取更方便。而且我们打印出他们3个引用的hashcode发现是同一个,这就是线程安全的体现。

    public String data(){
        //第1种方式:获取ValueStack的引用,通过request间接获取
        HttpServletRequest request1=ServletActionContext.getRequest();
        ValueStack vs1=(ValueStack)request1.getAttribute("struts.valueStack");
        System.out.println(vs1.hashCode());

        //第2种方式:获取ValueStack的引用,通过request间接获取
        ActionContext context=ActionContext.getContext();
        Map<String,Object> request2=(Map<String,Object>)context.get("request");
        ValueStack vs2=(ValueStack)request2.get("struts.valueStack");
        System.out.println(vs2.hashCode());

        //第3种方式:获取ValueStack的引用,通过actionContext直接获取
        ValueStack vs3=context.getValueStack();
        System.out.println(vs3.hashCode());

        return SUCCESS;
    }

——获取ValueStack的引用后,我们采用压栈操作存数据。

//压栈操作
vs3.push(new User("Andy",22));
vs3.push(new User("eric",23));

——我们可以看到后push的在栈顶。
这里写图片描述

——我们在jsp中运用的时候,操作如下,默认取第一个,当然可以指定索引。

<!-- 取ValueStack值,不需要#,它从最上面栈顶依次找,找到就行 -->
<s:property value="username"/><br> //结果是eric
// 这里有一个索引是[1],其实意思是把第一个栈顶的元素pop出去,如果是[2]那么就把最上面2个元素pop出去再取值,下方我们重新做试验
<s:property value="[1].username" /> //结果是Andy

——采用setValue存数据,第一个参数是表达式,加#表示存在ActionContext里,不加#表示存在ValueStack里面,存在ValueStack里面其实是给里面对应的变量赋值,也就是说如下面代码所示,如果ValueStack里面没有username变量,那么就会报错。紧接着上面的代码,我们发现我们存在ValueStack里面的test2其实是覆盖了原先有的eric(因为它是从栈顶开始的第一个username属性对应的值,被setValue覆盖了)。

vs3.setValue("#username", "test1");
vs3.setValue("username", "test2");

上面第一句代码产生的效果就是存在了ActionContext里面:
这里写图片描述

——采用set方法设置数据。

//set方法,如果栈顶是Map元素,直接keyvalue赋值过去,如果栈顶不是,那么就创建一个放在栈顶
vs3.set("s1", new User("tom",30));

然后在jsp中取值的时候,操作:

<!-- 取栈顶Map里面的数据 -->
<s:property value="s1.username" />

注意:如果s:property不指定的话默认取栈顶元素(或元素对应的地址)。

——我们来验证ValueStack取值时候的索引是把元素pop出去,而不是取第几个值的意思。

//压栈操作
vs3.push(new User("Andy",22));
vs3.push(new User("eric",23));

//setValue
vs3.setValue("#username", "test1");
vs3.setValue("username", "test2");

//set方法,如果栈顶是Map元素,直接key和value赋值过去,如果栈顶不是,那么就创建一个放在栈顶
vs3.set("s1", new User("tom",30));

——经过上面的代码操作,我们的ValueStack的从栈顶往下应该是Map、User(test2)、User(Andy)。如下图。
这里写图片描述

——接着,我们在jsp中取值,按照道理好像是我们先从栈顶向下取了test2,然后下标为1也就是第二个是Andy,然后下标为2也就是第三个是空的没有值

    <s:property value="username"/><br>
    <s:property value="[1].username" /><br>
    <s:property value="[2].username" /><br>

但是,事实上我们得到的值是:

test2
test2
Andy

——原理就在于,我们第一句话取值,表示从栈顶向下取第一个遇到的值,就是test2。第二句话取值,有个下标1,表示把栈顶第1个元素pop掉,然后生成一个新的ValueStack的List,从上向下取值,发现还是test2。第三句话取值,有个下标2,表示把栈顶最上面两个元素pop掉,也就是一个HashMap和User都pop掉了,然后取值,发现取到的是Andy。这里面起作用的函数就是cutStack,它根据下标的数字cut掉栈顶几个元素然后生成一个新的栈。

——另外,我们上面使用的struts标签s:property,其实本质上都是调用了findValue方法,这个寻找顺序和以前讲过的类似,就是page、request、session、application。

3、Struts2对EL表达式查找顺序的改变。核心意思就是:一般情况下,我们写${name}这样一个EL表达式,程序会先去pageScope中找,然后依次是requestScope、sessionScope和applicationScope。但是在Struts2种,查找顺序就不是这样了,而是pageScope、requestScope、ValueStack、ContextMap(ActionContext)、sessionScope和applicationScope。也就是说,我们在动作类中如果不把数据放入4个域中,一样可以通过EL表达式获取到数据,因为它会被放到ValueStack里。

——我们在动作类中写:

public class MyAction extends ActionSupport {
    private String name="动作类中的name";
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    ……
}

——然后,我们刷新页面发现会自动添加到ValueStack里面了。
这里写图片描述

——我们在jsp页面使用EL表达式${name }获取数据,就能获取到动作类中的name。核心是因为Struts2对原来的request进行了包装。

4、iterator操作符。

——现在动作类里面整一个List。

public class MyAction extends ActionSupport {
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public String data(){
        users=new ArrayList<User>();
        users.add(new User("Eric",20));
        users.add(new User("Andy",21));
        users.add(new User("Tom",22));

        return SUCCESS;
    }
}

——然后,在jsp中直接使用,我们说这个List没有放在4个域中,但是被放到了ValueStack里,所有在jsp中使用是可以取到值的。下面有两种方法,第一种是没有var属性的,那么每次遍历的元素都会被压到ValueStack里面,所以我们就直接用属性名username等取值。如果有var属性,那么会将它们放到ActionContext中,其中var的值作为Key,元素内容作为Value,所以我们取值需要用#,而且是#key+属性的形式取值。

  <table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
    </tr>
    <s:iterator value="users" status="stat">
        <tr>
            <td><s:property value="#stat.count" /></td>
            <td><s:property value="username" /></td>
            <td><s:property value="age" /></td>
        </tr>
    </s:iterator>
  </table>

  <table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
    </tr>
    <s:iterator value="users" var="u" status="stat">
        <tr>
            <td>${stat.count }</td>
            <td><s:property value="#u.username" /></td>
            <td><s:property value="#u.age" /></td>
        </tr>
    </s:iterator>
  </table>

5、OGNL的投影:添加过滤条件。很少用到,做个了解即可。

——比如上面迭代的例子,我们做如下修改,也就是获取所有age>21的元素。这里面有3种,?#表示全部,^#表示匹配第一个,$#表示匹配最后一个。

<s:iterator value="users.{?#this.age>21}" status="stat">

——下面表示只提取username属性的值。

<s:iterator value="users.{username}" status="stat">

6、struts2的其他标签。

——set标签。作用是var当做key,value里面数据当做值存到ActionContext里去。但是写成如下的样子,虽然在ActionContext里能找到,但是值是null。因为value里面是一个OGNL表达式,需要转成字符串。

<s:set var="name1" value="test1"></s:set>

正确写法:

<s:set var="name1" value="'test1'"></s:set>

可以看到ActionContext李:
这里写图片描述

——action标签。指定动作类名称,并不会执行,取值默认是false。

<s:action name="action1" executeResult="true"></s:action>

——if/else标签。

<s:set var="score" value="'C'"></s:set>
<s:if test="#score=='A'">优秀</s:if>
<s:elseif test="#score=='B'">良好</s:elseif>
<s:else>不妙</s:else>

——url标签。

// 页面上直接输出i-am-value
<s:url value="i-am-value"></s:url>
//把action全路径输出来,显示的是/Day01_ValueStack/data.action,不存储
<s:url action="data"></s:url>
//不显示,只存储,存在ActionContext里,key就是customUrl
<s:url action="data" var="customUrl"></s:url>

有的时候我们存储了action的全路径后,就使用<a href='<s:property value="#customUrl" />'>点我到action去</a>’。而不直接使用硬编码的<a href='${pageContext.request.contextPath}/data.action'>点我到action去</a>。因为我们时候要做伪静态页面,也就是把网址最后修改成html结尾。那么这个时候我们使用#customUrl就自动转换了,而采用硬编码的方式就会出错。

伪静态的设置在struts.xml里:

<constant name="struts.action.extensions" value="html" ></constant>

——我们还可以添加get参数:

    <s:url action="actionData" var="customUrl">
        <s:param name="username" value="'andy'"></s:param>
    </s:url>

然后我们在使用这个#customUrl链接的地方就会发现,这个链接后面自动添加了一个参数。
这里写图片描述

7、几种符号的使用。

——#。去contextMap里面的值用。还有在OGNL创建Map对象时用,如<s:radio list="#{'0':'男','1':'女'}" />

——$。jsp的EL表达式用。还有在struts.xml配置文件中也用,比如配置下载文件的那地方${@java.net.URLEncoder.encode(filename)}。这里的filename是我们在动作类里面定义的,我们知道动作类是默认在ValueStack的栈顶的,所以里面的变量也在ValueStack里面,所以我们才能取到值。

——%。用的地方是把字符串强制当做OGNL表达式。我们知道反过来把OGNL表达式转成字符串就是加一对单引号,反过来操作的话就是用这里的%{}。比如<s:textfield value="%{username}" />

8、结合ModelDriver、struts表单的源代码。

——在struts2-core-2.3.15.3.jar核心包里面有一个xhtml的包,里面就是UI主题,还有一个simple的主题。

——我们可以针对某一个表单或者全部表单进行设置。但是需要注意的是如果设置成simple主题的话,前面label名称没有了需要自己写,而且每个表单都不换行了需要自己换行。我们在struts.xml文件中设置常量效果是一样的,只不过设置常量就意味着整个项目的表单都会生效:<constant name="struts.ui.theme" value="simple"></constant>

<s:form action="registerAction" theme="simple">
……
</s:form>

源代码:JavaEE struts表单和ModelDriver以及主题

9、防止表单重复提交。我们防止重复提交的话,一般是通过验证码,第一次生成的时候,放一份验证码在Session里面,然后提交后比较了就删除session里面的验证码,这样再提交就没有比较就判断出错了,防止重复提交。

——在struts2里面,我们可以用tokenSession来防止重复提交,它的原理就是只处理第一次的提交,后面的直接忽略掉。我们先在表单中增加一个token的标签表单,是隐藏的。

    <s:form action="loginAction">
        <s:token></s:token>
        <s:textfield name="username" label="用户名"></s:textfield>
        <s:submit value="提交"></s:submit>
    </s:form>

然后,在struts.xml对应的action里面配置这个拦截器。

    <package name="p_name_1" extends="struts-default">
        <action name="loginAction" class="com.hello.web.action.MyAction" method="login">
            <interceptor-ref name="defaultStack"></interceptor-ref>
            <interceptor-ref name="tokenSession"></interceptor-ref>
            <result>/success.jsp</result>
        </action>
    </package>

我们在动作类的方法中写的一个输出语句,每次提交都会经过动作类打印这条语句,但是配置了以上代码之后,我们提交成功后,后退浏览器再提交的时候,语句就不打印了,也就是说只打印一次,动作类的方法只执行1次,这就是防止重复提交的内容。

源代码:JavaEE Struts2利用tokenSession防止重复提交

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值