美文网首页技术干货
(八)Struts2进阶之值栈详解

(八)Struts2进阶之值栈详解

作者: 秃头哥编程 | 来源:发表于2018-05-22 22:10 被阅读0次

    1.什么是值栈

    在Servlet中,我们都是把数据放在域对象中,然后在jsp页面中进行读取。那么在Struts2中,提供了另外一种存储机制,就是值栈,在action中把值放入值栈,在页面中进行读取。

    2.action和servlet的区别

    action:每次访问的时候都会创建一次。
    servlet:默认在第一次访问的时候创建,在生命周期里只会创建一次。

    我们创建一个LifeAction来看看

    @SuppressWarnings("serial")
    public class LifeAction extends ActionSupport {
        
        public LifeAction() {
            System.out.println("action创建了。。。。");
        }
        @Override
        public String execute() throws Exception {
            // 没有操作
            return NONE;
        }
    }
    

    在该类中有一个构造方法,每次创建action的时候都会打印信息。
    在struts.xml中配置action

    <action name="life" class="com.codeliu.action.LifeAction"></action>
    

    访问后,每次刷新页面,都会创建一个action对象。

    3.值栈的存储位置

    每次创建一个action对象,都会跟随着一个值栈对象,也就是说每个action里面都有一个值栈。

    4.获取值栈对象

    // 获取ActionContext实例对象
    ActionContext context = ActionContext.getContext();
    // 获取值栈对象
    ValueStack stack1 = context.getValueStack();
    ValueStack stack2 = context.getValueStack();
    // true 说明每个action只有一个值栈对象
    System.out.println(stack1 == stack2);
    

    上述代码通过ActionContext类获取一个ValueStack(值栈),同时说明了一个action里面只有一个值栈。

    5.值栈的内部结构

    值栈分为两部分。第一部分是root,第二部分是context,通过给成程序加断点,我们可以看到如下的结果

    27.png
    context是OgnlContext的实例对象,root是CompoundRoot的实例对象,我们看看这两个类的定义
    public class CompoundRoot extends ArrayList {......}
    
    public class OgnlContext extends Object implements Map {......}
    

    这说明root其实本质是一个ArrayList,而context本质是一个Map。

    root中一般存放数值,而context一般存放对象的引用。

    下面是context的存储示意图


    28.png

    通过debug标签,可以查看值栈的内部结构。(要使用Struts2的标签,必须在jsp开头引入标签库

    创建一个ValueStackAction.java

    @SuppressWarnings("serial")
    public class ValueStackAction extends ActionSupport {
        @Override
        public String execute() throws Exception {
            
            return SUCCESS;
        }
    }
    

    在struts.xml中配置Action

    <action name="valueStack" class="com.codeliu.action.ValueStackAction">
        <result name="success">/valueStack.jsp</result>
    </action>
    

    然后创建一个valueStack.jsp

    <body>
        <!-- 通过这个标签获取值栈的信息 -->
        <s:debug></s:debug>
    </body>
    

    运行这个action之后就能看到一个链接,点进去就能看到值栈的状态


    root的内部

    可以看到root内部的栈顶是一个action实例。

    action对象里有一个值栈对象,值栈对象里有action引用。

    6.向栈中放数据

    向值栈中放数据有三种方法,分别是使用set方法,push方法和使用action的变量,下面来看一下。

    (1)使用set方法

    public class ValueStackAction extends ActionSupport {
        @Override
        public String execute() throws Exception {
            /**
             * 往值栈中放数据的三种方法
             */
            // 第一种 使用set方法
            ActionContext context = ActionContext.getContext();
            ValueStack stack = context.getValueStack();
            stack.set("url", "www.codeliu.com");
            return SUCCESS;
        }
    }
    

    同样使用debug标签查看值栈的状态,发现栈顶元素变成了我们插入的数据Map


    栈顶元素已经改变了

    (2)使用push方法

    @SuppressWarnings("serial")
    public class ValueStackAction extends ActionSupport {
        @Override
        public String execute() throws Exception {
            /**
             * 往值栈中放数据的三种方法
             */
            ActionContext context = ActionContext.getContext();
            ValueStack stack = context.getValueStack();
            // 第二种 使用push方法
            stack.push("codeliu");
            return SUCCESS;
        }
    }
    

    再次查看,发现栈顶元素变成了一个String


    栈顶元素又变了

    (3)使用Action类的变量来放数据

    @SuppressWarnings("serial")
    public class ValueStackAction extends ActionSupport {
        // 1. 定义变量
        private String url;
        // 2. 记得要get方法
        public String getUrl() {
            return url;
        }
        @Override
        public String execute() throws Exception {
            /**
             * 往值栈中放数据的三种方法
             */
            // 3.设置值
            url = "www.codeliu.com";
            return SUCCESS;
        }
    }
    

    刷新查看值栈,结果如下


    栈顶没变,但action中的元素多了

    我们发现栈顶元素没有变,但action实例中多了一行。

    一般我们使用第三种方法更多,因为这样更节省空间,同时使用第三种方法,记得提供get方法,不然无法插入。

    7.从栈中读数据

    现在我们看看使用OGNL+Struts2的标签从值栈中读取使用上面第三种方法放入的数据。
    (1)读字符串的值

    @SuppressWarnings("serial")
    /**
     * 从值栈中获取字符串、对象和List集合
     * @author liu
     */
    public class GetValueFromValueStackAction extends ActionSupport {
        private String url;
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
        public GetValueFromValueStackAction() {}
        
        @Override
        public String execute() throws Exception {
            url = "www.codeliu.com";
            return SUCCESS;
        }
    }
    

    在struts.xml中增加一条记录

    <action name="getValue" class="com.codeliu.action.GetValueFromValueStackAction">
        <result>/getvalue.jsp</result>
    </action>
    

    创建getvalue.jsp

    <body>
        <!-- OGNL集合Struts2的标签获取值栈中的值 -->
        <s:property value="url"/>
    </body>
    

    启动tomcat后,进入网页就能看到输出了url的值。

    (2)读对象的值
    还记得我们上篇文章中有一个User类,里面有username和password属性,现在我们修改GetValueFromValueStackAction类

    @SuppressWarnings("serial")
    /**
     * 从值栈中获取字符串、对象和List集合
     * @author liu
     */
    public class GetValueFromValueStackAction extends ActionSupport {
        private User user = new User();
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
        public GetValueFromValueStackAction() {}
        @Override
        public String execute() throws Exception {
            user.setUsername("CodeTiger");
            user.setPassword("123456");
            return SUCCESS;
        }
    }
    

    在getvalue.jsp中添加如下代码

    <!-- 通过ognl获取user对象的值 -->
    <s:property value="user.username"/>
    <s:property value="user.password"/>
    

    运行后,输出我们设置的值。

    (3)读List集合的值
    修改GetValueFromValueStackAction类的代码如下

    @SuppressWarnings("serial")
    /**
     * 从值栈中获取字符串、对象和List集合
     * @author liu
     */
    public class GetValueFromValueStackAction extends ActionSupport {
        private User user = new User();
        private List<User> list = new ArrayList<User>();
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
        
        public List<User> getList() {
            return list;
        }
    
        public void setList(List<User> list) {
            this.list = list;
        }
    
        public GetValueFromValueStackAction() {}
        
        @Override
        public String execute() throws Exception {
            user.setUsername("CodeTiger");
            user.setPassword("123456");
            list.add(user);
            return SUCCESS;
        }
    }
    

    读取值栈中的List,一般有三种方法

    <!-- 获取List集合中的值 -->
        <!-- 第一种方式 -->
        <s:property value="list[0].username"/>
        <s:property value="list[0].password"/><br>
        <!-- 第二种方式 -->
        <s:iterator value="list">
            <s:property value="username"/>
            <s:property value="password"/>
        </s:iterator><br>
        <!-- 第三种方式 -->
        <s:iterator value="list" var="user">
            <!-- 每次遍历出来的user对象,都会放到context中去,使用OGNL取root中的数据,
                可以不用#,但取context中的数据,则要加上# 
            -->
            <s:property value="#user.username"/>
            <s:property value="#user.password"/>
        </s:iterator>
    

    注意第三种方式


    下篇文章讲OGNL,所以讲OGNL之前得先熟悉了解一下值栈,才能更好的使用OGNL。

    相关文章

      网友评论

        本文标题:(八)Struts2进阶之值栈详解

        本文链接:https://www.haomeiwen.com/subject/paphjftx.html