struts2_ognl表达式与值栈

Struts2

OGNL表达式

概述

OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个开源项目。Struts框架使用OGNL作为默认的表达式语言。

所谓表达式,就是用一定的格式字符串访问数据或方法。

表达式显示的内容如果脱离了解析引擎是毫无意义的。

OGNL提供了这种字符串解析的引擎

开发人员可以把重点放在如何写字符串上就可以达到调用方法或属性的功能

OGNL环境

Jar包

ognl-xxx.jar,javassist-xxx.jar

ognl:ognl的核心环境
javassist是ognl的依赖,提供字节码修改能力。

OGNL提供的功能

  • 支持对象方法的调用
  • 支持对象对象属性访问(getter方法属性化的转换)
  • 支持静态方法的调用
  • 支持集合对象操作

OGNL API使用

核心类和元素

  • 表达式:expression
  • 根对象:root
  • 上下文: OgnlContext
  • Ognl工具类: Ognl
表达式与根对象
  • 访问根对象的属性

    // 根对象
    User root = new User();
    root.setName("张三疯");
    root.setAge(19);
    // 表达式
    String expression="name";
    //通过Ognl方式获得根对象中的表达式结果
    Object value = Ognl.getValue(expression, root);

    Ognl在根对象中以表达式的方式取出根对象的属性

  • 访问根对象的方法

    // 根对象
    User root = new User();
    root.setName("张三疯");
    root.setAge(19);
    // 表达式
    String expression="getName()";
    //通过Ognl方式获得根对象中的表达式结果
    Object value = Ognl.getValue(expression, root);
表达式与上下文
  • 通过#+key修饰符的方式访问上下文中的对象

    // 上下文对象
    Map<Object, Object> context = new HashMap<Object, Object>();
    context.put("aaa", "小a");
    context.put("bbb", "小b");
    // 根对象
    Object root = null;
    // 表达式
    String expression = "#aaa";
    Object value = Ognl.getValue(expression, context, root);
表达式,根对象,上下文
  • 根对象与上下文结合

    // 上下文对象
    Map<Object, Object> context = new HashMap<Object, Object>();
    context.put("aaa", "小a");
    context.put("bbb", "小b");
    // 根对象
    User root = new User();
    root.setName("小a");
    root.setAge(19);
    // 表达式
    String expression = "name.equals(#aaa)";
    // 获取ognl结果
    Object value = Ognl.getValue(expression, context, root);
静态方法调用
// Integer.valueOf("123");
String expression = "@java.lang.Integer@valueOf(\"123\")";
Object value = Ognl.getValue(expression, null);
// Runtime.getRuntime().exec("calc.exe");
String expression = "@java.lang.Runtime@getRuntime().exec(\"calc.exe\")";
Object value = Ognl.getValue(expression, null);

静态方法调用中:

  • 类名必须使用全路径
  • 类前加@修饰
  • 调用的静态方法的@替代

ValueStack值栈

值栈的概念

ValueStack是一个接口,实现类是OgnlValueStack。值栈就是一个存取空间,理解为是一个数据的中转站。客户端发送一个请求到Action,服务器就会创建一个Action的实例。(Action是多例的,不会出现线程安全的问题。Servlet是单例的,会出现线程安全的问题。),只要服务器有一个Action的实例,就会创建一个值栈为这个Action服务。值栈对象贯穿了整个的Action的生命周期。值栈被Struts2保存到了request域中一份。

  • 值栈是数据的中转站
  • MVC模型中,Action负责Controller,在Action中将model数据存储到值栈中
  • MVC模型中,JSP负责View,view通过值栈去取数据

这里写图片描述

ActionContext和ValueStack的关系

每次请求中,都会在这个请求的过程中,创建一个ActionContext。ActionContext创建的同时,会创建一个ValueStack。ValueStack中的的上下文对象会传递给ActionContext,作为ActionContext的上下文。ValueStack中的上下文同时引用了自身ValueStack。

public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
        ActionContext ctx;
        Integer counter = 1;
        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (oldCounter != null) {
            counter = oldCounter + 1;
        }

        ActionContext oldContext = ActionContext.getContext();
        if (oldContext != null) {
            // detected existing context, so we are probably in a forward
            ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
        } else {
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
            ctx = new ActionContext(stack.getContext());
        }
        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
        ActionContext.setContext(ctx);
        return ctx;
}

ValueStack获取方式

  • ActionContext方式获取

    ValueStack valueStack = ActionContext.getContext().getValueStack();

    值栈是存储在自己内部的map中,自己的map又传递交给了ActionContext。

  • HttpServletRequest方式获取ValueStack

    ValueStack valueStack = ServletActionContext.getValueStack(ServletActionContext.getRequest());

    值栈也存储了一份到request中。

值栈的结构组成

  • root 区

    根对象。
    具体类型为CompoundRoot。
    CompoundRoot实现了List接口,其实就是一个list集合。

  • context 区

    上下文对象。
    具体类型为OgnlContext。
    OgnlContext内部操控的是一个Map集合,其实context区可以理解为一个Map集合。

值栈的debug查询展示
  • 在jsp页面中,引入struts的标签库

    <%@ taglib uri="/struts-tags" prefix="s"%>
  • 通过<s:debug/>标签可以在jsp中查看值栈的结构

这里写图片描述

上下文区存储的数据
  • request对象数据
  • response对象数据
  • application对象数据
  • parameters数据
  • attr数据:request,session,application中的atrribute数据。

值栈存储数据方式

值栈存储数据主要指的是往根对象区存储数据。根对象是一个List集合类型,存储着对象。
其中根对象还维护了一个Map负责存储key-value类型的数据

默认的值栈数据
  • DefaultTextProvider
  • 当前的action对象实例
push方式存取
  • 存储

    ValueStack valueStack = ActionContext.getContext().getValueStack();
    User user = new User();
    user.setName("李四");
    user.setAge(100);
    valueStack.push(user);      
  • 获取

    <s:property value="name" />
set方式存取
  • 存储

    ValueStack valueStack = ActionContext.getContext().getValueStack();
    User user = new User();
    user.setName("李四");
    user.setAge(100);   
    valueStack.set("user", user);
  • 获取

    <s:property value="user.name" />
属性驱动方式
  • 存储:自动通过valueStack存储到当前action的类中
  • 获取

    <s:property value="name" />
模型驱动方式
  • 存储:自动存储到valuestack的root区中
  • 获取

    <s:property value="name" />
EL 如何获得值栈中数据
  • struts在前端控制器中对请求进行了包装,包装为了StrutsRequestWrapper
  • EL表达式的核心内容其实就是对request的attribute属性进行访问,即request.getAttribute()方法
  • StrutsRequestWrapper对getAttribute方法进行了重写

    public Object getAttribute(String key) {
        if (key == null) {
            throw new NullPointerException("You must specify a key value");
        }
    
        if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
            // don't bother with the standard javax.servlet attributes, we can short-circuit this
            // see WW-953 and the forums post linked in that issue for more info
            return super.getAttribute(key);
        }
    
        ActionContext ctx = ActionContext.getContext();
        Object attribute = super.getAttribute(key);
    
        if (ctx != null && attribute == null) {
            boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));
    
            // note: we don't let # come through or else a request for
            // #attr.foo or #request.foo could cause an endless loop
            if (!alreadyIn && !key.contains("#")) {
                try {
                    // If not found, then try the ValueStack
                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
                    ValueStack stack = ctx.getValueStack();
                    if (stack != null) {
                        attribute = stack.findValue(key);
                    }
                } finally {
                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
                }
            }
        }
        return attribute;
    }

    通过struts的源码可以看出,getAttribute方法是先在request的attribuite中找,找不到就到ValueStack中去找

获取值栈上下文中的数据

<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#parameters.id"/>
<s:property value="#parameters.aaa"/>
<s:property value="#attr.name"/>

OGNL中的符号

#号的作用
  • 获取Context中的数据

    <s:property value="#request.name"/>
    <s:property value="#session.name"/>
  • 构建一个Map集合

    <s:iterator var="entry" value="#{ 'aa':'11','bb':'22','cc':'33' }">
    <s:property value="key"/>--<s:property value="value"/><br/>
    <s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/>
    </s:iterator>
%号的作用
  • 强制解析OGNL表达式

    <s:textfield value="%{#request.name}" name="name"/>
  • 强制不解析OGNL表达式

    <s:property value="%{'#request.name'}"/>
$号的作用
```

中文 :login.welcome=您好:{#session.user.username}  
英文 :login.welcome=Hello:
{#session.user.username}
“`

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值