java框架之Struts2(3)-OGNL&ValueStack

OGNL

概述

OGNL 是 Object-Graph Navigation Language 的縮寫,它是一種第三方的、功能強大的表達式語言,經過它簡單一致的表達式語法,能夠存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現字段類型轉化等功能。它使用相同的表達式去存取對象的屬性。html

OGNL : 對象圖導航語言,比 EL 表達式強大不少。
  • EL:從 11 個隱式對象中取值。
  • OGNL:可調用對象的方法,獲取 Struts2 中值棧的數據。

入門

java工程下使用

package com.zze.bean;

import java.util.Date;

public class User {
    private String name;
    private Integer age;
    private Date birthday;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}
com.zze.bean.User
@Test
public void test1() throws OgnlException {
    // 得到 context
    OgnlContext context = new OgnlContext();
    // 得到根對象
    Object root = context.getRoot();
    Object value = Ognl.getValue("'hello Struts2'.length()", context, root);
    System.out.println(value);

    /*
    13
     */
}
例:調用對象方法
@Test
public void test2() throws OgnlException {
    // 得到 context
    OgnlContext context = new OgnlContext();
    // 得到根對象
    Object root = context.getRoot();
    // 執行表達式:@類名@方法名
    Object value = Ognl.getValue("@java.lang.Math@random()", context, root);
    System.out.println(value);
    /*
    0.6367826736345159
     */
}
例:調用靜態方法
@Test
public void test3() throws OgnlException {
    // 得到 context
    OgnlContext context = new OgnlContext();
    User user = new User();
    user.setName("張三");
    user.setAge(12);
    context.setRoot(user);
    // 表達式直接寫放入 root 中對象的屬性名稱便可取到對應屬性名的值
    Object name = Ognl.getValue("name", context, context.getRoot());
    Object age = Ognl.getValue("age", context, context.getRoot());
    System.out.println(name);
    System.out.println(age);
    System.out.println(age.getClass());
    /*
    張三
    12
    class java.lang.Integer
     */
}
例:訪問 Root 中的數據
@Test
public void test4() throws OgnlException {
    // 得到 context
    OgnlContext context = new OgnlContext();
    User user = new User();
    user.setName("張三");
    user.setAge(12);
    context.put("user", user);
    // 表達式直接寫放入 context 中 #key 便可取到對應值
    Object name = Ognl.getValue("#user.name", context, context.getRoot());
    Object age = Ognl.getValue("#user.age", context, context.getRoot());
    System.out.println(name);
    System.out.println(age);
    /*
    張三
    12
     */
}
例:訪問 context 中的數據

Struts中使用

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<%--訪問對象方法--%>
<s:property value="'hello Struts2'.length()"/>

<hr>

<%--
訪問靜態方法
須要設置常量:struts.ognl.allowStaticMethodAccess = true
--%>
<s:property value="@java.lang.Math@random()"/>
</body>
</html>
例:訪問對象方法 & 靜態方法

特殊符號

#

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<%
    request.setAttribute("str", "托馬斯的小貨車");
%>
<s:property value="#request.str"/>
</body>
</html>
'#' 號能夠用來獲取值棧中 context 區域的數據
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<%--構建 List--%>
<s:iterator var="letter" value="{'a','b','c'}">
    <s:property value="letter"/>|<s:property value="#letter"/>
</s:iterator>
<hr>
<%--構建 Map--%>
<s:iterator var="entry" value="#{1:'aa',2:'bb',3:'cc'}">
    <s:property value="key"/>|<s:property value="#entry.key"/>
    <s:property value="value"/>|<s:property value="#entry.value"/>
    <br>
</s:iterator>
</body>
</html>
'#' 號能夠用來構建一個 Map

%

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<%
    request.setAttribute("name", "托馬斯");
%>

<s:property value="#request.name"/>
<br>
<%--
按以下嵌套標籤會報錯:
<s:textfield name="name" value="<s:property value="#request.name"/>" />
能夠利用 %{} 強制解析表達式
--%>

<s:textfield name="name" value="%{#request.name}" />
</body>
</html>

'%' 號能夠用來強制解析字符串爲表達式

$

<validators>
     <field name=」intb」>
             <field-validator type=」int」>
             <param name=」min」>10</param>
             <param name=」max」>100</param>
             < message>BAction-test校驗:數字必須爲${min}爲${max}之間!</message>
         </field-validator>
     </field>
</validators>
'$' 號能夠用來在配置文件中引用值棧中的值

ValueStack

概述

ValueStack 是 Struts 的一個接口,字面意義爲值棧,OgnlValueStack 是 ValueStack 的實現類,客戶端發起一個請求 Struts2 架構會建立一個 Action 實例同時建立一個 OgnlValueStack 值棧實例,OgnlValueStack 貫穿整個Action 的生命週期,Struts2 中使用 OGNL 將請求 Action 的參數封裝爲對象存儲到值棧中,並經過 OGNL 表達式讀取值棧中對象的屬性值。java

ValueStack 其實相似一個數據中轉站,Struts2 中的數據都保存在值棧中。web

值棧的內部結構

ValueStack 中有兩個主要的區域:apache

  • root:其實就是一個 ArrayList。
  • context:其實就是一個 Map。

    context 中放置了 web 開發中經常使用對象的引用,例如:session

        request:原生 Servlet 請求對象。架構

        session:會話對象。app

        application:ServletContext對象dom

        parameters:請求參數對象。jsp

        attr:依次在 request、session、application 尋找匹配值。ide

所說的操做值棧,一般指的是操做 ValueStack 中的 root 區域。

在 request、session、application 中存取值就至關於操做 ValueStack 的 context 區域。

值棧與ActionContext的關係

首先請求時會通過核心過濾器,查看核心過濾器的 doFilter 方法:

 1 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
 2 
 3     HttpServletRequest request = (HttpServletRequest) req;
 4     HttpServletResponse response = (HttpServletResponse) res;
 5 
 6     try {
 7         if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
 8             chain.doFilter(request, response);
 9         } else {
10             prepare.setEncodingAndLocale(request, response);
11             prepare.createActionContext(request, response);
12             prepare.assignDispatcherToThread();
13             request = prepare.wrapRequest(request);
14             ActionMapping mapping = prepare.findActionMapping(request, response, true);
15             if (mapping == null) {
16                 boolean handled = execute.executeStaticResourceRequest(request, response);
17                 if (!handled) {
18                     chain.doFilter(request, response);
19                 }
20             } else {
21                 execute.executeAction(request, response, mapping);
22             }
23         }
24     } finally {
25         prepare.cleanupRequest(request);
26     }
27 }
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter#doFilter

建立 ActionContext 就在第 11 行,查看 createActionContext 方法:

 1 public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
 2     ActionContext ctx;
 3     Integer counter = 1;
 4     Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
 5     if (oldCounter != null) {
 6         counter = oldCounter + 1;
 7     }
 8 
 9     ActionContext oldContext = ActionContext.getContext();
10     if (oldContext != null) {
11         // detected existing context, so we are probably in a forward
12         ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
13     } else {
14         ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
15         stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
16         ctx = new ActionContext(stack.getContext());
17     }
18     request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
19     ActionContext.setContext(ctx);
20     return ctx;
21 }
org.apache.struts2.dispatcher.ng.PrepareOperations#createActionContext

直接從 14 行開始看,在 14 行建立了值棧對象 stack ,接着在 16 行將 stack.getContext() 傳給了 ActionContext 來建立 ActionContext 實例,而 stack.getContext() 中擁有對值棧的引用,也就是說這部分執行完後在 ActionContext 中是直接能夠取到值棧的。

結論:ActionContext 之因此能訪問 Servlet 的 API ,是由於在其內部有值棧的引用,而值棧的 context 部分又擁有對 Servlet 經常使用對象(request、session、servletContext)的引用。

ValueStack的得到

經過上一節,已經知道是能夠經過 ActionContext 獲取到值棧的引用的。接着看核心過濾器的 doFilter 方法:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    try {
        if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
            chain.doFilter(request, response);
        } else {
            prepare.setEncodingAndLocale(request, response);
            prepare.createActionContext(request, response);
            prepare.assignDispatcherToThread();
            request = prepare.wrapRequest(request);
            ActionMapping mapping = prepare.findActionMapping(request, response, true);
            if (mapping == null) {
                boolean handled = execute.executeStaticResourceRequest(request, response);
                if (!handled) {
                    chain.doFilter(request, response);
                }
            } else {
                execute.executeAction(request, response, mapping);
            }
        }
    } finally {
        prepare.cleanupRequest(request);
    }
}
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter#doFilter

看到 21 行,這行用來開始執行 Action,查看 executeAction 方法:

1 public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
2     dispatcher.serviceAction(request, response, mapping);
3 }
org.apache.struts2.dispatcher.ng.ExecuteOperations#executeAction

接着看到  serviceAction 方法:

 1 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
 2         throws ServletException {
 3 
 4     Map<String, Object> extraContext = createContextMap(request, response, mapping);
 5 
 6     // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
 7     ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
 8     boolean nullStack = stack == null;
 9     if (nullStack) {
10         ActionContext ctx = ActionContext.getContext();
11         if (ctx != null) {
12             stack = ctx.getValueStack();
13         }
14     }
15     if (stack != null) {
16         extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
17     }
18 
19     String timerKey = "Handling request from Dispatcher";
20     try {
21         UtilTimerStack.push(timerKey);
22         String namespace = mapping.getNamespace();
23         String name = mapping.getName();
24         String method = mapping.getMethod();
25 
26         ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
27                 namespace, name, method, extraContext, true, false);
28 
29         request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
30 
31         // if the ActionMapping says to go straight to a result, do it!
32         if (mapping.getResult() != null) {
33             Result result = mapping.getResult();
34             result.execute(proxy.getInvocation());
35         } else {
36             proxy.execute();
37         }
38 
39         // If there was a previous value stack then set it back onto the request
40         if (!nullStack) {
41             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
42         }
43     } catch (ConfigurationException e) {
44         logConfigurationException(request, e);
45         sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
46     } catch (Exception e) {
47         if (handleException || devMode) {
48             sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
49         } else {
50             throw new ServletException(e);
51         }
52     } finally {
53         UtilTimerStack.pop(timerKey);
54     }
55 }
org.apache.struts2.dispatcher.Dispatcher#serviceAction

直接看 41 行,當值棧不爲空時,將值棧的引用放入了 request 域。

結論:除了經過 ActionContext 得到值棧,咱們還能夠經過 request 獲取到值棧。

因此在 Action 中咱們能夠經過以下代碼獲取值棧:

// 獲取值棧方式 1 、經過 ActionContext
ValueStack valueStack1 = ActionContext.getContext().getValueStack();

// 獲取值棧方式 2 、經過 request
// STRUTS_VALUESTACK_KEY = "struts.valueStack";
ValueStack valueStack2 = (ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

System.out.println(valueStack1 == valueStack2); // true

操做值棧

方法一:在Action中提供屬性的get方法

默認狀況下,Struts2 會將訪問的 Action 對象壓入值棧,因此在 Action 中提供的屬性會隨之存入值棧:

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

public class Test1Action extends ActionSupport {
    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    @Override
    public String execute() throws Exception {
        this.name = "張三";
        this.age = 19;
        return super.execute();
    }
}
Action
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<s:property value="name"/>
<s:property value="age"/>
</body>
</html>

jsp

方法二:手動調用值棧方法

咱們已經知道了如何在 Action 中獲取值棧,固然也能夠在 Action 中操做值棧:

package com.zze.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class Test2Action extends ActionSupport {
    @Override
    public String execute() throws Exception {
        ActionContext.getContext().getValueStack().set("name","張三");
        ActionContext.getContext().getValueStack().set("age",20);
        return super.execute();
    }
}
Action
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<s:property value="name"/>
<s:property value="age"/>
</body>
</html>

jsp

查看值棧數據

Struts2 爲方便咱們調試,給咱們提供了一個標籤,咱們用這個標籤能夠直接查看到值棧中的數據:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<s:debug/>
</body>
</html>

jsp

標籤獲取值棧數據

已經知道如何操做值棧,如今咱們看一下如何在頁面中獲取到值棧中的數據。

Struts2 爲簡易咱們在頁面中獲取值棧數據的操做,給咱們提供了一些標籤,看以下示例:

準備

package com.zze.bean;

import java.util.Date;

public class User {
    private String name;
    private Integer age;
    private Date birthday;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}
com.zze.bean.User
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true"/>
    <package name="test" extends="struts-default" namespace="/">
        <action name="*" class="com.zze.action.{1}Action">
            <result>/index.jsp</result>
        </action>
    </package>
</struts>
struts.xml

例1:root中獲取JavaBean對象

package com.zze.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.zze.bean.User;

public class Test1Action extends ActionSupport {
    @Override
    public String execute() throws Exception {
        User user = new User();
        user.setName("張三");
        user.setAge(29);
        // 將 user 壓入棧頂
        ActionContext.getContext().getValueStack().push(user);
        return super.execute();
    }
}
com.zze.action.Test1Action
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<%--可直接訪問棧頂對象屬性--%>
<s:property value="name"/>
<s:property value="age"/>

<s:debug/>
</body>
</html>

index.jsp

例2:root中獲取JavaBean對象集合

package com.zze.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.zze.bean.User;

import java.util.ArrayList;
import java.util.List;

public class Test2Action extends ActionSupport {
    @Override
    public String execute() throws Exception {
        User user1 = new User();
        user1.setName("張三");
        user1.setAge(29);
        User user2 = new User();
        user2.setName("李四");
        user2.setAge(30);

        List<User> userList = new ArrayList<>();
        userList.add(user1);
        userList.add(user2);

        ActionContext.getContext().getValueStack().set("userList",userList);
        return super.execute();
    }
}
com.zze.action.Test2Action
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<s:property value="userList[0].name"/>
<s:property value="userList[0].age"/>
<s:property value="userList[1].name"/>
<s:property value="userList[1].age"/>
<s:debug/>
</body>
</html>

index.jsp

例3:context中獲取域字段

package com.zze.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class Test3Action extends ActionSupport {

    @Override
    public String execute() throws Exception {
        ActionContext.getContext().put("name","張三");
        ActionContext.getContext().getSession().put("name","李四");
        ActionContext.getContext().getApplication().put("name","王五");
        return super.execute();
    }
}
com.zze.action.Test3Action
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:debug/>
</body>
</html>

index.jsp

獲取 root 區域中數據直接使用對象屬性名便可,若是是 map 則使用 key;獲取 context 中屬性需在 key 前加上 ‘#’。

EL獲取值棧數據

獲取值棧數據的方式除了上面經過 Struts2 提供的標籤的方式,還能夠經過 EL 表達式獲取,例如:

package com.zze.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.zze.bean.User;

public class Test4Action extends ActionSupport {

    @Override
    public String execute() throws Exception {
        User user = new User();
        user.setName("托馬斯");
        ActionContext.getContext().getValueStack().push(user);
        return super.execute();
    }
}
com.zze.action.Test4Action
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Test</title>
</head>
<body>
${name}

<s:debug/>
</body>
</html>

index.jsp

咱們知道,EL 表達式原本就只能獲取 11 個隱式對象中的數據,爲何在這裏還能獲取值棧中的數據呢?固然是 Struts2 作了手腳,依舊從核心過濾器開始查看源碼:

 1 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
 2 
 3     HttpServletRequest request = (HttpServletRequest) req;
 4     HttpServletResponse response = (HttpServletResponse) res;
 5 
 6     try {
 7         if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
 8             chain.doFilter(request, response);
 9         } else {
10             prepare.setEncodingAndLocale(request, response);
11             prepare.createActionContext(request, response);
12             prepare.assignDispatcherToThread();
13             request = prepare.wrapRequest(request);
14             ActionMapping mapping = prepare.findActionMapping(request, response, true);
15             if (mapping == null) {
16                 boolean handled = execute.executeStaticResourceRequest(request, response);
17                 if (!handled) {
18                     chain.doFilter(request, response);
19                 }
20             } else {
21                 execute.executeAction(request, response, mapping);
22             }
23         }
24     } finally {
25         prepare.cleanupRequest(request);
26     }
27 }
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter#doFilter

看到第 13 行,經過 prepare.wrapRequest(request) 將原生 request 進行了包裝,查看 wrapRequest 方法:

 1 public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {
 2     HttpServletRequest request = oldRequest;
 3     try {
 4         // Wrap request first, just in case it is multipart/form-data
 5         // parameters might not be accessible through before encoding (ww-1278)
 6         request = dispatcher.wrapRequest(request);
 7         ServletActionContext.setRequest(request);
 8     } catch (IOException e) {
 9         throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);
10     }
11     return request;
12 }
org.apache.struts2.dispatcher.ng.PrepareOperations#wrapRequest

繼續進入 dispatcher.wrapRequest 方法:

 1 public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
 2     // don't wrap more than once
 3     if (request instanceof StrutsRequestWrapper) {
 4         return request;
 5     }
 6 
 7     String content_type = request.getContentType();
 8     if (content_type != null && content_type.contains("multipart/form-data")) {
 9         MultiPartRequest mpr = getMultiPartRequest();
10         LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
11         request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);
12     } else {
13         request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
14     }
15 
16     return request;
17 }
org.apache.struts2.dispatcher.Dispatcher#wrapRequest

看 8-14 行,若是不是文件上傳類的請求,將會執行第 13 行,也就是說普通狀況下請求 Action 該方法返回的 request 就是 StrutsRequestWrapper 的實例,查看該類:

 1 package org.apache.struts2.dispatcher;
 2 
 3 import com.opensymphony.xwork2.ActionContext;
 4 import com.opensymphony.xwork2.util.ValueStack;
 5 
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletRequestWrapper;
 8 
 9 import static org.apache.commons.lang3.BooleanUtils.isTrue;
10 
11 public class StrutsRequestWrapper extends HttpServletRequestWrapper {
12 
13     private static final String REQUEST_WRAPPER_GET_ATTRIBUTE = "__requestWrapper.getAttribute";
14     private final boolean disableRequestAttributeValueStackLookup;
15 
16 
17     public StrutsRequestWrapper(HttpServletRequest req) {
18         this(req, false);
19     }
20 
21 
22     public StrutsRequestWrapper(HttpServletRequest req, boolean disableRequestAttributeValueStackLookup) {
23         super(req);
24         this.disableRequestAttributeValueStackLookup = disableRequestAttributeValueStackLookup;
25     }
26 
27 
28     public Object getAttribute(String key) {
29         if (key == null) {
30             throw new NullPointerException("You must specify a key value");
31         }
32 
33         if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
34             return super.getAttribute(key);
35         }
36 
37         ActionContext ctx = ActionContext.getContext();
38         Object attribute = super.getAttribute(key);
39 
40         if (ctx != null && attribute == null) {
41             boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));
42 
43             if (!alreadyIn && !key.contains("#")) {
44                 try {
45                     // If not found, then try the ValueStack
46                     ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
47                     ValueStack stack = ctx.getValueStack();
48                     if (stack != null) {
49                         attribute = stack.findValue(key);
50                     }
51                 } finally {
52                     ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
53                 }
54             }
55         }
56         return attribute;
57     }
58 }
org.apache.struts2.dispatcher.StrutsRequestWrapper

能夠看到這個類其實就是將 getAttribute 方法進行了重寫,當經過該方法獲取一個值時,若是經過原生 request 未獲取到,則繼續從值棧中尋找這個 key 對應的值並返回。

而咱們經過 EL 表達式獲取值實際上也會調用 request.getAttribute 方法,此時 Struts2 對該方法進行了包裝加強,這就是使用 EL 能獲取到值棧數據的緣由。

相關文章
相關標籤/搜索