Struts2-6 OGNL表达式

  在HelloWorld实例中,通过${productName}表达式直接获取productName值;而在打印request域对象(<%=request %>)时,发现其并非原生的javax.servlet.http.HttpServletRequest,而是经Struts2封装后的org.apache.struts2.dispatcher.StrutsRequestWrapper对象;由此可见,productName属性值并不在request等域对象中,而是从值栈(ValueStack)中获取的……


一、ValueStack

 可以从ActionContext中获取ValueStack对象,其内部分为两个逻辑部分

  • Map栈:实际是OgnlContext类型,是对ActionContext的一个引用,保存着各种各样的Map对象;
  • 对象栈:实际是CompoundRoot类型,是采用ArrayList定义的数据结构意义上的栈,保存着各种与Action相关的对象。
这里写图片描述 这里写图片描述

那么该如何从值栈中获取对象属性呢?


二、OGNL表达式

  Struts2利用s:property标签和OGNL(Object-Graph Navigation Language,对象-图导航语言) 表达式来读取值栈中的属性值。
  property标签的属性如下:

这里写图片描述

1. 读取ObjectStack中对象的属性

 1). 访问其中对象的属性:object.productName、object['productName']、object.["productName"];

 2). 引用对象栈中的对象:

  • 可以采用从零开始的下标来引用对象栈中的对象,即栈顶对象用[0]来引用,其下面的那个对象用[1]来引用,如[0].message
  • [n]的含义是从第n个对象开始搜索,而不是只搜索第n个对象;
  • 若从栈顶对象开始搜索,则可以省略下标部分;
  • 结合s:property标签:<s:property value="[0].message"><s:property value="message">

注意:默认情况下,Struts2自动将Action对象放到值栈的栈顶。

……
<!-- 在JSP页面可以导入Struts2提供的标签 -->
<%@ taglib prefix="s" uri="/struts-tags" %>

<body>
    <!-- JSP页面:利用Struts2的调试功能,显示值栈的内容 -->
    <s:debug></s:debug>
    <!-- JSP页面:利用Struts2的property标签,输出其中某对象的属性值 -->
    <s:property value="[0].productName"/>   
    <s:property value="productDesc"/>   
</body>
/**
 * 创建对象,将其压入到值栈的对象栈中      
 */
// 1. 获取ValueStack
ValueStack stack = ActionContext.getContext().getValueStack();
// 2. 创建TestProduct对象,并为其属性赋值
TestProduct object = new TestProduct();
object.setProductName = "spring";
object.setProductDesc = "java";
// 3. 将TestProduct对象压入值栈
stack.push(object);
2. 读取ContextMap中对象的属性

 1). 访问其中对象的属性:#object.proName、#object['proName']、#object.["proName"]
 
 2). 示例:

  • 返回session中的code属性:#session.code
  • 返回request中的customer属性的name属性:#request.customer.name
  • 返回域对象(按request、session、application的顺序)的lastAccessData属性:#attr.lastAccessDate
3. 使用OGNL调用字段和方法

 1). 调用任何公共Java类的公共静态字段或静态方法:

  • 调用字段:@fullyQualifiedClassName@fieldName,如@java.util.Calendar@DECEMBER
  • 调用方法:@fullyQualifiedClassName@methodName(argumentList),如@app4.Util@now()

注意:默认情况下,Struts2不允许调用任意Java类的静态方法,需要在struts.xml文件中进行如下配置:

<!-- 打开静态方法调用的限制 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>

 2). 调用ObjectStack中某对象的属性或方法:

  • 调用字段:object.fieldName,如[0].datePattern
  • 调用方法:object.methodName(argumentList),如[0].repeat(3, "Hello")

示例代码如下:

<!-- 1). 使用OGNL调用任何public类的public类型的静态字段或静态方法 -->
<s:property value="@java.lang.Math@PI"/>        <br><br>
<s:property value="@java.lang.Math@cos(0)"/>    <br><br>

<!-- 2). 使用OGNL调用ObjectStack中某个对象的属性或方法 -->
<s:property value="setProductName('java')"/>    <br><br>
ProductName:<s:property value="productName"/>   <br><br>
4. 使用OGNL访问数组类型的属性

 有些属性将返回对象数组而不是单个对象,可以采用如下方式读取对象数组的元素数和各个属性:

<!-- 调用数组对象的属性 -->
<%
    String[] names = new String[]{"qiaobc", "qiaob", "qiaoc"};
    request.setAttribute("names", names);
%>
length: <s:property value="#attr.names.length"/>    <br><br>
names[1]: <s:property value="#attr.names[1]"/>      <br><br>
5. 使用OGNL访问List类型的属性
  • 采用下标的方式直接访问List中的指定元素,如nameList[0]
  • 调用其size关键字或size()方法获取List的长度,如nameList.sizenameList.size()
  • 调用其isEmpty关键字或isEmpty()方法判断List是否为空;
  • 可以采用OGNL表达式创建List,与生命Java数组相同,如{"Red", "Green", "Black"}
6. 使用OGNL访问Map类型的属性
<!-- 使用OGNL访问Map类型的属性 -->
<%
    Map<String, String> letters = new HashMap<String, String>();
    request.setAttribute("letters", letters);
    letters.put("qiaobc", "qiaob");
    letters.put("qiaob", "qiaobc");
%>

<!-- 1). 读取Map类型的属性:返回结果为“{qiaob=qiaobc, qiaobc=qiaob}” -->
qiaobc: <s:property value="#attr.letters"/>             <br><br>
<!-- 2). 读取Map中某个key的值:mapObject[keyString] -->
qiaobc: <s:property value="#attr.letters['qiaobc']"/>   <br><br>

<!-- 3). 读取Map对象的长度 -->
size: <s:property value="#attr.letters.size()"/>        <br><br>
<!-- 4). 判断Map对象是否为空 -->
isEmpty: <s:property value="#attr.letters.isEmpty"/>    <br><br>

<!-- 5). 创建Map:#{key1:value1, key2:value2, key3:value3} -->
7. 使用EL访问值栈中对象的属性

 原理:Struts2将HttpServletRequest对象包装为org.apache.struts2.dispatcher.StrutsRequestWrapper对象传到页面上,而该类重写了getAttribute(String attrName)方法;
 示例:<s:property value="fieldName"> 等价于EL表达式的 ${fieldName}
 注意:EL表达式可以实现的,OGNL均能实现,但反之不然。


三、声明式异常

1. exception-mapping元素

 用于在struts.xml文件中配置当前action请求的声明式异常处理,其包含两个属性:

  • exception属性:指定需要捕获的异常类型,即异常的全类名;
  • result属性:指定响应结果,即当捕获该异常时执行当前响应结果;其结果可以来自当前action的result声明,也可来自global-results声明。
<action name="product-save" class="com.qiaobc.struts2.domain.Product" method="save">
    <!-- exception-mapping配置算术异常:当save方法中发生算术异常时触发 -->
    <exception-mapping result="input" exception="java.lang.ArithmeticException"></exception-mapping>
    <result name="input">/WEB-INF/pages/input.jsp</result>
    <result name="details">/WEB-INF/pages/details.jsp</result>
</action>
2. 声明式异常处理机制

 声明式异常由ExceptionMappingInterceptor拦截器负责处理,当捕获异常时拦截器向ValueStack的对象栈栈顶添加com.opensymphony.xwork2.interceptor.ExceptionHolder对象,其包含两个属性:

  • exceptionStack属性:包含被捕获异常的栈,其实是以getter()方法的形式存在的;
  • exception属性:表示被捕获异常的Exception对象。
<!-- 在异常处理页面上通过Struts2提供的debug标签来查看对象栈栈顶的异常信息 -->
<s:debug></s:debug> <br><br>

<!-- 显示对象栈栈顶ExceptionHolder对象的exceptionStack和exception属性信息 -->
<s:property value="exceptionStack"/>                                <br><br>
<s:property value="exception"/> --> ${exception }                  <br><br>
<s:property value="exception.message"/> --> ${exception.message }  <br><br>

  源代码分析:

/**
 * 默认情况下的异常拦截器,在struts-default.xml文件中默认配置
 */
public class ExceptionMappingInterceptor extends AbstractInterceptor {

    // ……

    /**
     * 重写父类的拦截方法
     */
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        String result;

        try {
            result = invocation.invoke();  // 按顺序执行其他拦截器并执行Action方法
        } catch (Exception e) {
            if (isLogEnabled()) {
                handleLogging(e);
            }
            List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
            // 发生异常时,获取exception-mapping元素的配置信息
            ExceptionMappingConfig mappingConfig = this.findMappingFromExceptions(exceptionMappings, e);
            if (mappingConfig != null && mappingConfig.getResult()!=null) {
                Map parameterMap = mappingConfig.getParams();
                // create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
                invocation.getInvocationContext().setParameters(new HashMap<String, Object>(parameterMap));
                result = mappingConfig.getResult();
                // 发布异常(创建了封装有异常信息的ExceptionHolder对象)
                publishException(invocation, new ExceptionHolder(e));
            } else {
                throw e;
            }
        }

        return result;
    }

    // ……

    /**
     * 发布异常
     */
    protected void publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder) {
        // 将封装有异常信息的ExceptionHolder对象压入值栈,故可从值栈中获取异常信息
        invocation.getStack().push(exceptionHolder); 
    }

    // ……
}
3. global-exception-mappings元素

  采用global-exception-mappings元素可为应用程序添加全局性的异常捕获映射,但其声明的异常映射只能应用在global-results元素下声明的某个result元素。

<!-- 注意两者的相对位置 -->
<global-results>
    <result name="input">/WEB-INF/pages/input.jsp</result>
</global-results>

<global-exception-mappings>
    <exception-mapping result="input" exception="java.lang.ArithmeticException"></exception-mapping>
</global-exception-mappings>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值