(十)Struts2进阶之OGNL在Struts2中的使用

上篇文章把OGNL单独拿出来讲了,这篇文章就讲讲OGNL结合Struts2的使用。

Struts2中OGNL表达式必须配合Struts2标签使用,不然没什么效果。

(八)Struts2进阶之值栈详解这篇文章中我们分析了值栈的结构,讲了值栈的实现类是OgnlValueStack类,它包含两部分,分别是root(对应CompoundRoot类)和context(对应OgnlContext类)。

在每次调用核心控制器时,在它的doFilter方法中有这么一行代码

prepare.createActionContext(request, response);

这行对于我们理解值栈很关键啊,本来应该放在值栈里说的。。。

首先先捋一捋ActionContext是什么玩意,上篇文章单纯讲OGNL的时候说了,它有一个执行的上下文环境OgnlContext,它里面有一个根对象和若干个普通对象。

那么在Struts2中,要使用OGNL,也得给它一个上下文环境,其实我看网上很多文章说ActionContext是上下文环境,但我认为应该是OgnlValueStack类,它里面有这么两行代码

CompoundRoot root;
transient Map<String, Object> context;

分别代表值栈的root和context。不过我们可以在ActionContext类中取得值栈对象。

下面开始说。

1.Struts2中的OGNL

Struts2中的OGNL比单纯的OGNL要更强大,上篇文章中,我们说了只能有一个root对象,而在OGNL中,可以有多个root对象。root对应的实现类是CompoundRoot,该类实质是List,所有能有多个root对象。在Strtus2中的Root使用的是CompoundRoot对象,而CompoundRoot继承了ArrayList,所以他可以存储一系列的对象,这些对象可以看作是OGNL中的root对象。当我们当问某个属性时,CompoundRootAccessor对象实例会负责在CompoundRoot对象中找到包含我们指定属性的对象。

2.再说ValueStack

对于用户的每个action请求,Struts2在执行相应的方法之前,都会新建一个valuestack对象,其实这个创建的过程就在Struts2的核心控制器里的doFilter方法中,也就是从上面的那行代码开始的,它会把request、session、application、atrr、parameters放入context中。

valueStack的内部包含两个逻辑部分,一个叫做Object Stack(root),另一个叫做Context Map(context)。Struts2将动作和相关对象压入Object Stack,把各种各样的映射关系(Map类型的对象)压入Context Map。其中的Object Stack中的对象都相当于OGNL中的”root”对象,因此对他们可以直接访问。如果要访问Context Map中的对象,那么就得在OGNL表达式前面加上”#”符号。如果没有加”#”,那么Struts2默认会在Object Stack中进行搜索。
36.jpg

Strut2会把下面的这些映射关系压入到Context Map中:
(1) parameters:这个Map中包含当前请求的请求参数
(2) request:包含当前请求的所有属性
(3) session:包含当前请求的会话的所有属性
(4) application:包含当前应用程序的ServletContext属性
(5) attr:这个Map用来按照这个顺序来检索某个属性:request、session、application
注意:请求参数总是返回一个String类型的数组。比如我们要想知道请求参数的个数,那么正确的表达式应该是#parameters.count[0],而不是#parameters.count。

3.使用OGNL获取值栈中的属性值

34.png

获取root中的值,我们不需要加#符号,这和上篇文章是一样的道理。

下面看看代码
新建一个User类,有username和password两个属性

@SuppressWarnings("serial")
public class User implements Serializable{
    private String username;
    private String password;

    public User() {}

    public User(String username, String password) {
        super();
        this.username = username;
        this.password = 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;
    }
}

新建一个OGNLAction类,实现了三个接口,分别用于获取request,session和application,另外还有一个list。

@SuppressWarnings("serial")
public class OGNLAction extends ActionSupport implements RequestAware, SessionAware, ApplicationAware {
    // 这里的值会被放入Object Stack(root)中,记得提供get方法,不然无法取值
    private List<User> s1;
    // 这里的值会被放入Context Map(context)中
    private Map<String, Object> applicationMap;
    // 这里的值会被放入Context Map(context)中
    private Map<String, Object> sessionMap;
    // 这里的值会被放入Context Map(context)中
    private Map<String, Object> requestMap;
    @Override
    public String execute() throws Exception {
        User u1 = new User("liu", "12345");
        User u2 = new User("xu", "123456");
        User u3 = new User("guo", "520520");
        s1 = new ArrayList<User>();
        Collections.addAll(s1, u1, u2, u3);
        applicationMap.put("users", s1);
        sessionMap.put("users", s1);
        requestMap.put("users", s1);
        return SUCCESS;
    }

    @Override
    public void setApplication(Map<String, Object> application) {
        this.applicationMap = application;
    }

    @Override
    public void setRequest(Map<String, Object> request) {
        this.requestMap = request;
    }

    @Override
    public void setSession(Map<String, Object> session) {
        this.sessionMap = session;
    }

    public List<User> getS1() {
        return s1;
    }

    public void setS1(List<User> s1) {
        this.s1 = s1;
    }
}

在struts.xml中配置action

<action name="OGNL" class="com.codeliu.action.OGNLAction">
    <result>/index.jsp</result>
</action>

新建一个index.jsp

<body>
    <s:debug></s:debug>
    <s:property value="s1[0].username"/> or <s:property value="#request.users[1].username"/>
    or <s:property value="#application.users[0].username"/> or <s:property value="#session['users'][0].username"/>
    <br>
    <!-- 遍历list中的元素 -->
    <s:iterator value="s1" var="user">
        <!-- 说明使用var属性后,值放在了context Map中,要通过#去获取 -->
        <s:property value="#user.username"/>&nbsp;<s:property value="#user.password"/><br>
        <!-- 不加#也能获取到,说明在root中没有找到,回去 context Map中找-->
        <s:property value="username"/>&nbsp;<s:property value="password"/><br>
    </s:iterator>

    <!-- 遍历applicationMap中的元素 -->
    <s:iterator value="#application.users" var="user">
        <s:property value="#user.username"/>&nbsp;<s:property value="#user.password"/><br>
    </s:iterator>

    <!-- 遍历requestMap中的元素 -->
    <s:iterator value="#request.users">
        <s:property value="username"/>&nbsp;<s:property value="password"/><br>
    </s:iterator>

    <!-- 遍历sessionMap中的元素 -->
    <s:iterator value="#session.users">
        <s:property value="username"/>&nbsp;<s:property value="password"/><br>
    </s:iterator>
</body>

启动tomcat后,结果如下
38.png
代码上面的注释都写的很清楚,说明什么问题?

1.除了List,其他的三个变量的值都放进去了Context Map中,要取出来必须加上#
2.使用遍历标签,如果使用了var属性,则每次遍历的值都放入了Context Map中,使用#符号也可以取出来。(不使用也可以,因为它在root中没找到,应该就会去Context Map中找。)
3.action类中,放入root中的数据,比如上面的list,记得要有相应的get方法,否则无法取到值。
4.使用#session[‘users’][0].username也可以获取到属性的值

4.调用静态方法和静态字段

其实在Struts2中OGNL调用静态方法、静态字段和上篇文章中讲的都一样。

要注意的是,Struts2默认是关闭调用静态方法的功能,所以要使用,得先打开。

<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>

对于值栈中的静态属性和方法,直接使用它们的名称即可。

    <!-- 调用Java API的静态变量和静态方法 -->
    <s:property value="@java.lang.Math@PI"/>
    <br>
    <s:property value="@java.lang.Math@floor(4.3)"/><br>

    <!-- 调用自己写的类中的静态字字段和静态方法 -->
    <s:property value="myName"/><br>
    <s:property value="getMyName()"/>

5.投影和过滤

和上篇文章讲的也一样。

6.# $ %的使用

“#”的作用
(1)访问非root对象的属性。例如:#session[“userName”]
(2)对集合进行投影与选择(具体可看上篇文章)
(3)构造对象(具体可看上篇文章),
“%”的作用
在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式
“$”的作用
(1)在配置文件中引用OGNL表达式(访问Action的属性)。
(2)在国际化资源文件中引用OGNL表达式(学习国际化时会学到)

7.this指针

在很多编程语言中,都有this指针的概念,它表示调用当前函数(方法)的对象。那么在OGNL中也有类似的概念。
我们已经学过,OGNL表达式是以”.”进行串联的的一个串字符串表达式。这个表达式在被执行的时候,从左到右,每一次计算都会返回一个临时的当前对象,并在此临时对象上再次进行调用,直到执行完毕。这个临时的当前变量就存储在一个叫做this的变量中,这个this变量我们就叫它this指针。通过使用this指针,我们可以使OGNL更加灵活,更加强大。
注:使用this指针时,必须在this前面加”#”,即this指针必须以“#this”的形式出现。
例如:group.userList.size().(#this+1).toString()

上篇文章中讲投影和过滤的时候使用了this指针



参考文章
https://blog.csdn.net/xiaokang123456kao/article/details/59483038
https://blog.csdn.net/yu102655/article/details/52182801

猜你喜欢

转载自blog.csdn.net/a_helloword/article/details/80440917
今日推荐