写在前面:Struts2主要包含前端控制器FC、Action、ValueStack容器、Result、Interceptor拦截器、Tags标签6个核心组件,本文主要介绍这几个组件的合作关系,由此构成Struts2的工作流程,以便更好地理解和使用Struts2。


背景知识

1、Struts2基础标签

    1)用于输出的标签
        --*<s:property value="name"/>    value的值实际上是OGNL表达式
        --特殊用法
            <s:property />    用于输出栈顶数据
    2)用于调试的标签
        <s:debug/>
        --该标签仅仅是给程序员用的,用于调试,即用于观察ValueStack中的数据
        --当代码提交给测试、客户时,要把该标签删除
        --该标签具有互斥性(可以理解为BUG),如果页面上出现多次这个标签时,实际上只有第一个有效
    3)用于循环的标签
        a、用于循环集合
            --比如Action中有集合属性List<User> users
            <s:iterator value="users">
                <s:property value="userName"/>
            </s:iterator>
        b、用于循环数字
            <s:iterator begin="1" end="3" var="i">
                <s:property value="#i"/>
            </s:iterator>

2、OGNL表达式

    1)类似于EL表达式,但是比EL功能强大
    2)表达式允许我们通过一个字符串,来访问JAVA对象。从而避免了在JSP上嵌套着写JAVA代码。即,表达式可以通过字符串间接的帮助我们访问JAVA对象。
    3)基本原理
        --OGNL表达式将被OGNL引擎解析,这个过程是Struts2自行完成的
        --OGNL引擎允许我们访问两种类型的对象,一种是JavaBean类型,称之为root对象;另一种为Map类型,称之为context对象。
        --当OGNL表达式以#开头时,OGNL引擎会访问context对象,否则会访问root对象
    4)root对象
        --我们写OGNL表达式时(不以#开头的情况),首先要明确谁是root对象,其次从该对象的下级属性写起。
        举例:
        --比如以Action为root对象,Action中有String name, User user属性,那么要在页面显示这两个属性的话,写法如下:
            <s:property value="name"/>
            <s:property value="user.userName"/>
        --比如以User为root对象,要在页面上显示它的名称,写法如下:
            <s:property value="userName"/>
    5)context对象
        --OGNL表达式以#开头,则访问context对象
        --#后面写的是context对象的key,context对象是Map类型

3、ValueStack

    1)什么是ValueStack
        Struts2中并不是直接将OGNL组件搬过来使用,而是使用一个对象容器ValueStack对这个组件进行了改造及封装,因此我们在Struts2中是通过ValueStack来使用OGNL表达式。并且ValueStack中封装了Action的数据,上下文等数据,这些数据我们都可以通过OGNL表达式来获得。
    2)原理
        --结构:ValueStack中封装了OGNL引擎、context对象、Stack对象。
        --规则:当我们在标签中写一个OGNL表达式时,该表达式被Struts2自动的传递给ValueStack中的OGNL引擎,引擎会判断表达式是否以#开头,若是则直接从context对象中取值,否则向Stack中取值。
        --如何向Stack取值:OGNL表达式从栈顶向下依次取值,它是以栈的每一级对象作为root对象从中取值的,若取到值则直接返回,否则继续向下取值,直到栈底为止。
    3)*栈顶的变化
        a、默认情况下,栈顶是Action
        *b、在循环时,栈顶将发生变化,规则如下:
            --在未循环时,栈顶依然是Action
            --在第一次循环时,Struts2将循环变量(如user)压入到栈中,因此循环变量占据栈顶位置,而Action处于栈的第二位。
            --在第n次循环时,Struts2将第n-1次的循环变量从栈顶移除,然后再将本次循环的变量压入栈中,因此本次循环的变量占据栈顶位置,而Action依然处于栈的第二位。
            --当最后一次循环结束时,Struts2将最后一次循环的变量从栈顶移除,而没有新的变量压入栈中,那么Action再次成为栈顶。
            简而言之:
                --在循环过程中,栈顶是循环变量
                --循环结束后,栈顶是Action
    4)*总结(OGNL,ValueStack)
        a、在没有循环时,栈顶是Action,即可以以Action为root来写OGNL表达式
        b、在循环时,栈顶是循环变量,栈第二位是Action
            --循环集合
                栈顶是集合中的对象,那么可以以该对象为root,来写OGNL表达式,即直接从该root对象的下级属性写起
                举例:Action中有属性List<User> users,在页面上循环users
                <s:iterator value="users">
                    <s:property value="userName"/>
                </s:iterator>
                即:循环过程中,栈顶是User对象,以它为root来写OGNL表达式的话,就写userName
            --循环数字
                栈顶是循环变量(数字),但是由于我们不能以数字为root(以JavaBean为root)来写OGNL表达式,因为数字已经是基本类型数据,没有下级属性让我们访问。如果我们想直接输出栈顶可以用<s:property/>,如果我们想引用循环变量,则需要写<s:property value="#i"/>,从context对象中依然可以取得这个值。
                举例:从1循环到3
                <s:iterator begin="1" end="3" var="i">
                    <s:property value="#i"/>
                </s:iterator>


工作原理

总结8句话,方便记忆:
        请求提交控制器,根据配置找Action。创建VS栈容器,实例Action放栈顶。
        调用Action算输出,历经层层拦截器。根据方法返回值,调用Result做输出。

详细流程如下(结合图示理解):
1、请求传入前端控制器FC
2、前端控制器FC根据Struts.xml配置文件查找Action
3、创建ValueStack值栈容器
4、创建Action并存入ValueStack栈顶
5、6、7、FC调用并执行Action逻辑,此过程可能先被Interceptor拦截器拦截处理
8、9、FC根据Action返回的result通过Result组件跳转到相应的JSP界面
10、在解析JSP时,若发现OGNL表达式,则向ValueStack容器中的OGNL引擎传入表达式,按照先前介绍的方式查询对应的值
11、OGNL引擎返回查找到的值给JSP,待所有数据加载完成,呈现JSP(通过Tags标签)界面

Tips:当重新提交请求(比如刷新界面)时,Action、ValueStack均会被重新创建。






转载请注明出处:

http://blog.csdn.net/daijin888888/article/details/51461043





Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐